Migrate from Make to n8n: routers, iterators, error handlers
Make and n8n look alike: both draw workflows as connected boxes. The differences are underneath, in how data flows between modules and how errors are handled. That's where migrations go wrong. This guide focuses on those differences, with a worked example, and then covers testing and cutover.
outline {
If you're coming from Zapier instead, use the Zapier to n8n guide. The cutover method is the same; the mapping is different.
What you need
- Access to your Make organisation, with rights to export scenarios.
- A production n8n instance with a public HTTPS URL (Self-host n8n).
- A spreadsheet for the inventory.
Step 1: Export and list your scenarios
For each scenario in Make, open it, click the ... menu and choose Export Blueprint. That downloads a JSON file describing every module, filter and setting. n8n can't import it, but it's the most accurate record of what the scenario does, and you can search it.
# List the modules each blueprint uses (needs jq)
for f in blueprints/*.json; do
echo "== $f"
jq -r '.. | objects | select(has("module")) | .module' "$f" | sort | uniq -c
doneMake's blueprint format isn't formally documented, so treat that query as a starting point. It assumes each module object has a module key (for example http:ActionSendData). Open one file and check that before you rely on the output.
Then fill in a row per scenario: name, schedule or trigger, modules used, operations per month (from the scenario's history or your usage page), owner, and keep, merge or drop.
Check it worked: every active scenario has a blueprint file and a row, and you've picked three low-risk ones to migrate first.
Step 2: Understand the one big difference: bundles and items
In Make, a module outputs bundles, and each bundle runs through the rest of the scenario separately. In n8n, a node outputs a list of items, and the next node processes all of them in one go.
Most of the time this works the same way in practice. It matters in three places:
- Iterators aren't needed for most arrays. If a field holds an array you want to split into separate items, use Split Out. After that, every node handles each item automatically.
- Aggregators become Aggregate or Summarize. To combine items back into one (for example, a list of line items into one email), use Aggregate. To total or group values, use Summarize.
- Counting is different. Make counts operations, roughly one per module per bundle. n8n counts one execution per workflow run, however many items or nodes it has.
Step 3: Map modules and tools
| Make | n8n | Notes |
|---|---|---|
| Instant trigger (webhook) | Webhook | Check the payload paths: n8n puts the body under body. |
| Scheduled trigger | Schedule Trigger | Supports intervals and cron. |
| Router | Connect one output to several nodes, or use Switch | Several connections run every branch. Switch sends each item down one matching branch. |
| Filter between modules | Filter or If | If keeps the false branch, Filter drops it. |
| Iterator | Split Out | Splits an array field into items. |
| Array aggregator | Aggregate | |
| Numeric or table aggregator | Summarize | Sum, count, average, grouped by a field. |
| Set variable or Set multiple variables | Edit Fields (Set) | |
| Data store | Data tables (built in, where available) or a Postgres or Google Sheets node | |
| HTTP module | HTTP Request | Paste a cURL command to fill it in. |
| Tools: Sleep | Wait | |
| Text parser, functions in mapping | Expressions and the Code node | n8n expressions accept JavaScript. |
| Webhook response | Respond to Webhook |
Check it worked: for each of your first three scenarios, you've written the n8n node next to every Make module.
Step 4: Translate error handlers
Make attaches error handler routes to modules, with directives like Resume, Ignore, Break, Rollback and Commit. n8n has no direct equivalent of each, but you can get the same behaviour:
| Make directive | What it does in Make | n8n approach |
|---|---|---|
| Ignore | Skips the failed bundle and carries on | Set the node's On Error to Continue (using error output) and leave the error output unconnected, or connect it to a logging node |
| Resume | Substitutes fallback output and carries on | Use the error output, add an Edit Fields node with the fallback values, and merge it back with a Merge node |
| Break | Stores the run as incomplete and retries it later | Turn on Retry On Fail on the node. For longer outages, log failed items and replay them from a separate workflow |
| Rollback | Stops and rolls back modules that support transactions | No equivalent. Design the steps so a partial run is safe, for example write to the database last |
| Commit | Stops and keeps what's done | Stop and Error after the steps you want to keep |
Then set an Error workflow in each workflow's settings, so failures that aren't handled send you an alert. The reliability guide shows how to build one.
Check it worked: for each error route in the blueprint, you've decided which n8n approach replaces it.
Step 5: Worked example: order lines into one summary
A common Make scenario: a webhook receives an order with a lines array, an Iterator splits the lines, a module looks up each product, and an Array aggregator builds one email.
In n8n:
- Webhook (
POST /orders). - Split Out, with Fields To Split Out set to
body.lines. One item per line. - HTTP Request to look up each product. This runs once per item automatically, with no loop node.
- Aggregate, with Individual Fields and the input field
name(or All Item Data), which turns the items back into one item holding a list. - Send Email, with a body built from the list:
Order {{ $('Webhook').item.json.body.id }}:
{{ $json.name.join('\n') }}$('Webhook').item reads data from an earlier node, which replaces Make's habit of mapping any earlier module directly.
Check it worked: a test order with three lines produces one email listing all three products.
Step 6: Test side by side, then cut over
Use the same method as for Zapier:
- Run in parallel. Keep the Make scenario live. Activate the n8n workflow with its final write steps pointed at test destinations. Send the same events to both.
- Compare daily. Count runs, then check a sample of records field by field.
- Cut over at a quiet time. Switch n8n's write steps to the real destinations, point the source at n8n's production webhook URL, and turn the Make scenario off without deleting it.
- Keep a way back. To roll back, turn the scenario on and point the webhook back at Make. Keep the blueprint file in version control.
When every scenario is moved and has run cleanly for a few weeks, check your Make billing cycle and downgrade or cancel.
Check it worked: n8n's executions show the real traffic, and the Make scenario's history shows no new runs.
Troubleshooting
- Too many emails, one per item: you need an Aggregate node before the email node.
- Only the first item is used: an expression like
$('Node').first()reads only one item. Use$('Node').item, or.all()in a Code node. - Router branches run in an unexpected order: n8n runs branches one after another, not at the same time. Order is shown in the workflow settings (execution order
v1). Don't make one branch depend on another finishing first. Merge them with a Merge node instead. - Webhook data paths differ: look at a real input in the execution view, and fix paths to
body.field.
References
- n8n data structure (items): https://docs.n8n.io/data/data-structure/
- Split Out: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.splitout/
- Aggregate: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.aggregate/
- Summarize: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.summarize/
- Merge: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.merge/
- Error handling: https://docs.n8n.io/flow-logic/error-handling/
- Make help: error handling and blueprints: https://help.make.com/