Skip to content

In n8n, copy this file and paste it onto the canvas, or use Import from File.

{
  "name": "23 · Zapier/Make to n8n Migration Kit (starter)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "mig/convert",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "m23-convert-api",
      "name": "Convert API",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "course-23-convert"
    },
    {
      "parameters": {
        "httpMethod": "GET",
        "path": "mig/mappings",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "m23-mappings-api",
      "name": "Mappings API",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        400
      ],
      "webhookId": "course-23-mappings"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "s1",
              "name": "app_map",
              "value": "={{ {\"webhook\": {\"trigger\": [\"n8n-nodes-base.webhook\",2],\"action\": [\"n8n-nodes-base.httpRequest\",4.2]},\"http\": {\"action\": [\"n8n-nodes-base.httpRequest\",4.2]},\"schedule\": {\"trigger\": [\"n8n-nodes-base.scheduleTrigger\",1.2]},\"gmail\": {\"trigger\": [\"n8n-nodes-base.gmailTrigger\",1.3],\"action\": [\"n8n-nodes-base.gmail\",2.1],\"credential\": \"Gmail\"},\"googlesheets\": {\"trigger\": [\"n8n-nodes-base.googleSheetsTrigger\",1],\"action\": [\"n8n-nodes-base.googleSheets\",4.5],\"credential\": \"Google Sheets\"},\"googledrive\": {\"trigger\": [\"n8n-nodes-base.googleDriveTrigger\",1],\"action\": [\"n8n-nodes-base.googleDrive\",3],\"credential\": \"Google Drive\"},\"googlecalendar\": {\"trigger\": [\"n8n-nodes-base.googleCalendarTrigger\",1],\"action\": [\"n8n-nodes-base.googleCalendar\",1.3],\"credential\": \"Google Calendar\"},\"slack\": {\"trigger\": [\"n8n-nodes-base.slackTrigger\",1],\"action\": [\"n8n-nodes-base.slack\",2.3],\"credential\": \"Slack\"},\"hubspot\": {\"trigger\": [\"n8n-nodes-base.hubspotTrigger\",1],\"action\": [\"n8n-nodes-base.hubspot\",2.1],\"credential\": \"HubSpot\"},\"airtable\": {\"trigger\": [\"n8n-nodes-base.airtableTrigger\",1],\"action\": [\"n8n-nodes-base.airtable\",2.1],\"credential\": \"Airtable\"},\"notion\": {\"trigger\": [\"n8n-nodes-base.notionTrigger\",1],\"action\": [\"n8n-nodes-base.notion\",2.2],\"credential\": \"Notion\"},\"stripe\": {\"trigger\": [\"n8n-nodes-base.stripeTrigger\",1],\"action\": [\"n8n-nodes-base.stripe\",1],\"credential\": \"Stripe\"},\"shopify\": {\"trigger\": [\"n8n-nodes-base.shopifyTrigger\",1],\"action\": [\"n8n-nodes-base.shopify\",1],\"credential\": \"Shopify\"},\"typeform\": {\"trigger\": [\"n8n-nodes-base.typeformTrigger\",1],\"credential\": \"Typeform\"},\"calendly\": {\"trigger\": [\"n8n-nodes-base.calendlyTrigger\",1],\"credential\": \"Calendly\"},\"telegram\": {\"trigger\": [\"n8n-nodes-base.telegramTrigger\",1.1],\"action\": [\"n8n-nodes-base.telegram\",1.2],\"credential\": \"Telegram\"},\"discord\": {\"action\": [\"n8n-nodes-base.discord\",2],\"credential\": \"Discord\"},\"trello\": {\"trigger\": [\"n8n-nodes-base.trelloTrigger\",1],\"action\": [\"n8n-nodes-base.trello\",1],\"credential\": \"Trello\"},\"mailchimp\": {\"trigger\": [\"n8n-nodes-base.mailchimpTrigger\",1],\"action\": [\"n8n-nodes-base.mailchimp\",1],\"credential\": \"Mailchimp\"},\"pipedrive\": {\"trigger\": [\"n8n-nodes-base.pipedriveTrigger\",1],\"action\": [\"n8n-nodes-base.pipedrive\",1],\"credential\": \"Pipedrive\"},\"openai\": {\"action\": [\"@n8n/n8n-nodes-langchain.openAi\",1.8],\"credential\": \"OpenAI\"},\"setvariable\": {\"action\": [\"n8n-nodes-base.set\",3.4]},\"delay\": {\"action\": [\"n8n-nodes-base.wait\",1.1]},\"code\": {\"action\": [\"n8n-nodes-base.code\",2]} } }}",
              "type": "object"
            },
            {
              "id": "s2",
              "name": "app_aliases",
              "value": "={{ {\"gateway\": \"webhook\",\"webhooks\": \"webhook\",\"catchhook\": \"webhook\",\"googleemail\": \"gmail\",\"googlemail\": \"gmail\",\"googlesheet\": \"googlesheets\",\"hubspotcrm\": \"hubspot\",\"openaigpt3\": \"openai\",\"chatgpt\": \"openai\",\"util\": \"setvariable\",\"json\": \"code\",\"formatter\": \"code\",\"regexp\": \"code\",\"javascript\": \"code\",\"sleep\": \"delay\",\"scheduler\": \"schedule\"} }}",
              "type": "object"
            },
            {
              "id": "s3",
              "name": "hours_mapped",
              "value": 0.25,
              "type": "number"
            },
            {
              "id": "s4",
              "name": "hours_partial",
              "value": 0.75,
              "type": "number"
            },
            {
              "id": "s5",
              "name": "hours_manual",
              "value": 2,
              "type": "number"
            },
            {
              "id": "s6",
              "name": "hours_testing",
              "value": 1,
              "type": "number"
            },
            {
              "id": "s7",
              "name": "hourly_rate",
              "value": 75,
              "type": "number"
            },
            {
              "id": "s8",
              "name": "zapier_cost_per_1000_tasks",
              "value": 30,
              "type": "number"
            },
            {
              "id": "s9",
              "name": "make_cost_per_10000_ops",
              "value": 11,
              "type": "number"
            },
            {
              "id": "s10",
              "name": "n8n_monthly_cost",
              "value": 20,
              "type": "number"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "m23-settings-edit-me",
      "name": "Settings (edit me)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        240,
        200
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "r1",
                    "leftValue": "={{ $('Convert API').isExecuted }}",
                    "rightValue": "",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "convert"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "r2",
                    "leftValue": "={{ $('Mappings API').isExecuted }}",
                    "rightValue": "",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "mappings"
            }
          ]
        },
        "options": {}
      },
      "id": "m23-route",
      "name": "Route",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        460,
        200
      ]
    },
    {
      "parameters": {
        "jsCode": "// Read a Make blueprint or a Zapier export and turn every scenario/zap into the same step tree:\n// { key, label, app, action, kind: trigger|action|router|iterator|aggregator, params, filter, routes }\nconst body = $('Convert API').first().json.body ?? {};\nconst parse = (v) => (typeof v === 'string' ? JSON.parse(v) : v);\nconst appKey = (s) => String(s ?? '').replace(/@.*$/, '').replace(/API$/i, '').replace(/V\\d+$/i, '').toLowerCase().replace(/[^a-z0-9]/g, '');\n\n// --- Make: blueprint.flow[] with module \"app:action\", mapper/parameters, filter on the module, router routes\nfunction makeSteps(flow, isRoot) {\n  return (flow ?? []).map((m, i) => {\n    const [app, action] = String(m.module ?? '').split(':');\n    const kind = m.module === 'builtin:BasicRouter' ? 'router' : m.module === 'builtin:BasicFeeder' ? 'iterator'\n      : /Aggregator$/.test(action ?? '') ? 'aggregator' : isRoot && i === 0 ? 'trigger' : 'action';\n    return {\n      key: String(m.id), label: m.metadata?.designer?.name || m.label || `${app} ${action}`,\n      app: appKey(app), app_raw: app, action: action ?? '', kind,\n      params: { ...(m.parameters ?? {}), ...(m.mapper ?? {}) },\n      filter: m.filter ? { label: m.filter.name ?? 'Filter', groups: (m.filter.conditions ?? []).map((g) => g.map((c) => ({ a: c.a, op: c.o, b: c.b }))) } : null,\n      routes: kind === 'router' ? (m.routes ?? []).map((r, j) => ({ label: r.flow?.[0]?.filter?.name || `Route ${j + 1}`, steps: makeSteps(r.flow, false) })) : null,\n    };\n  });\n}\n\n// --- Zapier: zapfile { zaps: [{ title, nodes: { id: { selected_api, action, type_of, params, parent_id } } }] }\nconst zapOp = { exact: 'text:equal', iexact: 'text:equal', contains: 'text:contain', icontains: 'text:contain', gt: 'number:greater', lt: 'number:less', exists: 'exist', not_exists: 'notexist', is_true: 'boolean:true', is_false: 'boolean:false' };\nfunction zapSteps(nodes) {\n  const all = Object.values(nodes ?? {});\n  const kids = (id) => all.filter((n) => String(n.parent_id ?? '') === String(id ?? '')).sort((a, b) => Number(a.id) - Number(b.id));\n  const filterOf = (n) => ({ label: n.title || 'Filter', groups: (n.params?.filter ?? n.params?.filter_criteria ?? []).map((g) => g.map((c) => ({ a: c.key, op: zapOp[c.match] ?? c.match, b: c.value }))) });\n  function chain(start, isRoot) {\n    const out = [];\n    let pendingFilter = null;\n    for (let n = start, i = 0; n; i++) {\n      const app = appKey(n.selected_api);\n      if (app === 'filter') { pendingFilter = filterOf(n); n = kids(n.id)[0]; continue; }\n      const children = kids(n.id);\n      const isPaths = /^(branching|paths)/.test(app);\n      out.push({\n        key: String(n.id), label: n.title || `${app} ${n.action ?? ''}`.trim(), app, app_raw: n.selected_api, action: n.action ?? '',\n        kind: isPaths ? 'router' : n.type_of === 'read' && isRoot && i === 0 ? 'trigger' : 'action',\n        params: n.params ?? {}, filter: pendingFilter,\n        routes: isPaths ? children.map((c) => ({ label: c.title || 'Path', filter: c.params?.filter ? filterOf(c) : null, steps: chain(kids(c.id)[0], false) })) : null,\n      });\n      pendingFilter = null;\n      if (isPaths) break;\n      n = children[0];\n    }\n    return out;\n  }\n  return chain(kids(null)[0] ?? all.find((n) => !n.parent_id), true);\n}\n\nlet scenarios = [], error = null;\ntry {\n  if (body.blueprint || body.blueprints) {\n    for (const bp of (body.blueprints ?? [body.blueprint]).map(parse))\n      scenarios.push({ name: bp.name || 'Make scenario', source: 'make', steps: makeSteps(bp.flow, true) });\n  } else if (body.zapfile || body.zap) {\n    const zf = parse(body.zapfile ?? { zaps: [parse(body.zap)] });\n    for (const z of zf.zaps ?? []) scenarios.push({ name: z.title || `Zap ${z.id}`, source: 'zapier', steps: zapSteps(z.nodes) });\n  } else error = 'send \"blueprint\" (Make export), \"blueprints\" (array) or \"zapfile\" / \"zap\" (Zapier export)';\n} catch (e) { error = `could not read the export: ${e.message}`; }\nif (!error && !scenarios.some((s) => s.steps.length)) error = 'no steps found in the export';\n\nconst runs = body.monthly_runs ?? {};\nreturn [{ json: { ok: !error, error, scenarios: scenarios.map((s) => ({ ...s, monthly_runs: Number(typeof runs === 'number' ? runs : runs[s.name] ?? 1000) })),\n  create: body.create === true, dry_run: body.dry_run === true } }];\n"
      },
      "id": "m23-read-export",
      "name": "Read Export",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        700,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// What the converter knows: apps with trigger/action equivalents and their aliases.\nconst s = $('Settings (edit me)').first().json;\nconst apps = Object.entries(s.app_map).map(([app, m]) => ({ app, trigger: m.trigger?.[0] ?? null, action: m.action?.[0] ?? null, credential: m.credential ?? null,\n  aliases: Object.entries(s.app_aliases).filter(([, v]) => v === app).map(([k]) => k) }));\nreturn [{ json: { count: apps.length, apps } }];\n"
      },
      "id": "m23-list-mappings",
      "name": "List Mappings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        700,
        400
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {}
      },
      "id": "m23-respond-mappings",
      "name": "Respond Mappings",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        920,
        400
      ]
    },
    {
      "parameters": {
        "content": "## Your task\nExports are already read into one step tree (try `samples/make-blueprint.json` as `blueprint`), and the mapping table is in Settings.\n1. **Convert to n8n** (Code): per step, look up `app_map` (trigger or action node), rewrite `{{1.email}}` / `{{1__email}}` to `$('Step').item.json.email` (webhooks: `.body.email`), filters → If, routers/paths → Switch (all matching outputs), unknown apps → No Op placeholder; lay nodes out and connect them.\n2. **Build Report**: mapped/partial/manual, credentials, effort hours, old platform cost, checklist, Markdown.\n3. Valid? → respond (400 otherwise). Optional: create the workflows with the **n8n** node (API credential).\n\nTest: `./test/test.sh`",
        "height": 260,
        "width": 420
      },
      "id": "starter-task-note",
      "name": "Your Task",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -340
      ]
    }
  ],
  "connections": {
    "Convert API": {
      "main": [
        [
          {
            "node": "Settings (edit me)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mappings API": {
      "main": [
        [
          {
            "node": "Settings (edit me)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Settings (edit me)": {
      "main": [
        [
          {
            "node": "Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route": {
      "main": [
        [
          {
            "node": "Read Export",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "List Mappings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Export": {
      "main": [
        []
      ]
    },
    "List Mappings": {
      "main": [
        [
          {
            "node": "Respond Mappings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {}
}