This workflow follows the Form → Form Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Client Onboarding (ep09)",
"nodes": [
{
"parameters": {
"formTitle": "New client intake \u2014 Northgate Labs",
"formDescription": "Five minutes now saves us both a week of email.",
"formFields": {
"values": [
{
"fieldLabel": "Company name",
"placeholder": "Acme Inc.",
"requiredField": true
},
{
"fieldLabel": "Contact name",
"placeholder": "Who signs off?",
"requiredField": true
},
{
"fieldLabel": "Contact email",
"fieldType": "email",
"requiredField": true
},
{
"fieldLabel": "Project type",
"fieldType": "dropdown",
"fieldOptions": {
"values": [
{
"option": "Automation retainer"
},
{
"option": "Website build"
},
{
"option": "Ongoing support"
}
]
},
"requiredField": true
},
{
"fieldLabel": "Kickoff date",
"fieldType": "date",
"requiredField": true
},
{
"fieldLabel": "Main goal for the first 90 days",
"fieldType": "textarea",
"requiredField": true
}
]
},
"options": {}
},
"id": "e9000000-0000-4000-8000-000000000001",
"name": "New client intake",
"type": "n8n-nodes-base.formTrigger",
"typeVersion": 2.2,
"position": [
200,
300
]
},
{
"parameters": {
"jsCode": "// One folder per client, plus a welcome doc from a template.\nconst fs = require('fs');\nconst path = require('path');\nconst base = $env.EP09_DIR;\nconst j = $input.first().json;\n\nconst slug = j['Company name'].toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');\nconst dir = path.join(base, 'clients', slug);\nfs.mkdirSync(dir, { recursive: true });\n\nlet doc = fs.readFileSync(path.join(base, 'templates', 'welcome-template.md'), 'utf8');\ndoc = doc.replace(/{{company}}/g, j['Company name'])\n .replace(/{{contact}}/g, j['Contact name'])\n .replace(/{{goal}}/g, j['Main goal for the first 90 days'])\n .replace(/{{kickoff}}/g, String(j['Kickoff date']).slice(0, 10));\nfs.writeFileSync(path.join(dir, 'welcome.md'), doc);\n\nj.slug = slug;\nj.dir = dir;\nreturn $input.all();"
},
"id": "e9000000-0000-4000-8000-000000000002",
"name": "Client folder + welcome doc",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
420,
300
]
},
{
"parameters": {
"jsCode": "// Contract from a template. SAMPLE ribbon stays in \u2014 fictional client, fictional terms.\nconst fs = require('fs');\nconst path = require('path');\nconst base = $env.EP09_DIR;\nconst j = $input.first().json;\n\nlet html = fs.readFileSync(path.join(base, 'templates', 'contract-template.html'), 'utf8');\nhtml = html.replace(/{{company}}/g, j['Company name'])\n .replace(/{{contact}}/g, j['Contact name'])\n .replace(/{{project}}/g, j['Project type'])\n .replace(/{{kickoff}}/g, String(j['Kickoff date']).slice(0, 10))\n .replace(/{{date_today}}/g, new Date().toISOString().slice(0, 10));\nfs.writeFileSync(path.join(j.dir, 'contract.html'), html);\nreturn $input.all();"
},
"id": "e9000000-0000-4000-8000-000000000003",
"name": "Contract from template",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
640,
300
]
},
{
"parameters": {
"jsCode": "// A real calendar invite: 45 minutes, 10:00 local, on the kickoff date.\nconst fs = require('fs');\nconst path = require('path');\nconst j = $input.first().json;\n\nconst d = String(j['Kickoff date']).slice(0, 10).replace(/-/g, '');\nconst ics = [\n 'BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//Northgate Labs//Onboarding//EN',\n 'BEGIN:VEVENT', 'UID:' + j.slug + '-kickoff@shipsitself.local',\n 'DTSTART:' + d + 'T100000', 'DTEND:' + d + 'T104500',\n 'SUMMARY:Kickoff - ' + j['Company name'],\n 'DESCRIPTION:First 90 days: ' + j['Main goal for the first 90 days'].replace(/\\n/g, ' '),\n 'END:VEVENT', 'END:VCALENDAR',\n].join('\\r\\n');\nfs.writeFileSync(path.join(j.dir, 'kickoff.ics'), ics);\nreturn $input.all();"
},
"id": "e9000000-0000-4000-8000-000000000004",
"name": "Kickoff invite (.ics)",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
860,
300
]
},
{
"parameters": {
"jsCode": "// One CRM: a CSV I can open, sort, and grep. Header written on first run.\nconst fs = require('fs');\nconst path = require('path');\nconst base = $env.EP09_DIR;\nconst j = $input.first().json;\n\nconst file = path.join(base, 'crm', 'crm.csv');\nif (!fs.existsSync(file)) {\n fs.writeFileSync(file, 'Company,Contact,Email,Project,Kickoff,Status,Created\\n');\n}\nconst esc = (v) => '\"' + String(v).replace(/\"/g, '\"\"') + '\"';\nconst row = [\n j['Company name'], j['Contact name'], j['Contact email'], j['Project type'],\n String(j['Kickoff date']).slice(0, 10), 'Onboarding', new Date().toISOString(),\n].map(esc).join(',');\nfs.appendFileSync(file, row + '\\n');\nreturn $input.all();"
},
"id": "e9000000-0000-4000-8000-000000000005",
"name": "CRM row",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1080,
300
]
},
{
"parameters": {
"jsCode": "// Task checklist with real dates: T+1, T+3, T+7 from kickoff.\nconst fs = require('fs');\nconst path = require('path');\nconst base = $env.EP09_DIR;\nconst j = $input.first().json;\n\nconst kick = String(j['Kickoff date']).slice(0, 10);\nconst plus = (n) => {\n const t = new Date(kick);\n t.setDate(t.getDate() + n);\n return t.toISOString().slice(0, 10);\n};\nlet md = fs.readFileSync(path.join(base, 'templates', 'checklist-template.md'), 'utf8');\nmd = md.replace(/{{company}}/g, j['Company name'])\n .replace(/{{contact}}/g, j['Contact name'])\n .replace(/{{kickoff}}/g, kick)\n .replace(/{{t1}}/g, plus(1)).replace(/{{t3}}/g, plus(3)).replace(/{{t7}}/g, plus(7));\nfs.writeFileSync(path.join(j.dir, 'tasks.md'), md);\nreturn $input.all();"
},
"id": "e9000000-0000-4000-8000-000000000006",
"name": "Task checklist",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1300,
300
]
},
{
"parameters": {
"operation": "completion",
"completionTitle": "Onboarding complete",
"completionMessage": "Folder, welcome doc, contract, calendar invite, CRM entry, and task checklist were just created.",
"options": {}
},
"id": "e9000000-0000-4000-8000-000000000007",
"name": "Done page",
"type": "n8n-nodes-base.form",
"typeVersion": 1,
"position": [
1520,
300
]
}
],
"connections": {
"New client intake": {
"main": [
[
{
"node": "Client folder + welcome doc",
"type": "main",
"index": 0
}
]
]
},
"Client folder + welcome doc": {
"main": [
[
{
"node": "Contract from template",
"type": "main",
"index": 0
}
]
]
},
"Contract from template": {
"main": [
[
{
"node": "Kickoff invite (.ics)",
"type": "main",
"index": 0
}
]
]
},
"Kickoff invite (.ics)": {
"main": [
[
{
"node": "CRM row",
"type": "main",
"index": 0
}
]
]
},
"CRM row": {
"main": [
[
{
"node": "Task checklist",
"type": "main",
"index": 0
}
]
]
},
"Task checklist": {
"main": [
[
{
"node": "Done page",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Client Onboarding (ep09). Uses formTrigger, form. Event-driven trigger; 7 nodes.
Source: https://github.com/Ships-Itself/builds/blob/main/ep09-client-onboarding/workflow.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This interactive tutorial teaches you how to build in n8n from scratch, using a live walkthrough with real-time examples. Rather than static documentation, this guided workflow explains key n8n concep
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
This workflow exposes an n8n Form that calculates a product’s selling price from cost, tax, and marketplace fees using either a target profit margin or a target markup, then displays the resulting pri
Credentials Transfer. Uses form, httpRequest, executeCommand, readWriteFile. Event-driven trigger; 22 nodes.
This workflow collects scheduled social post details via an n8n form and creates a Google Drive package containing a tweet.txt file and 1–4 numbered images, while validating inputs, preventing duplica