{
  "name": "Run New Client Onboarding End to End from a Single Form",
  "nodes": [
    {
      "parameters": {
        "content": "# One form, the whole onboarding\n\nA client signs. Somebody makes a folder, somebody books the kickoff, somebody sends the welcome email, somebody adds them to the tracker, and somebody tells the team. Usually the same somebody, usually late, usually missing one of them.\n\nFill in one form and all five happen in about four seconds.\n\n**Sequence**\n1. Form is submitted when a client signs\n2. Names, folder paths and dates are derived once, consistently\n3. A client folder is created in Google Drive\n4. The kickoff call goes in the calendar with the client invited\n5. The welcome email goes out with the folder link in it\n6. The client is appended to the register\n7. The team is told in Slack\n\n**You need**\n- Google Drive\n- Google Calendar\n- Gmail\n- Google Sheets\n- Slack\n\nBuilt by Better Automations (Melbourne, Australia).\nhttps://www.betterautomations.com.au/services/n8n-workflow-automation",
        "height": 700,
        "width": 460,
        "color": 4
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000001",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [-660, -180]
    },
    {
      "parameters": {
        "content": "## Why everything derives in one node\n\n**Derive Everything** builds the folder name, the kickoff date and the client code once, and every node downstream reads from it.\n\nThat is the difference between a folder called `Acme Pty Ltd` and one called `acme-pty-ltd-2026` appearing in the same Drive six months apart. Naming drifts the moment two people do it by hand, and it is close to impossible to fix later.",
        "height": 380,
        "width": 360,
        "color": 7
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000002",
      "name": "Sticky Note - Naming",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [280, -520]
    },
    {
      "parameters": {
        "content": "## Set the parent folder first\n\nIn **Create Client Folder**, pick the Drive folder your client folders live under. Leave it unset and folders land loose in My Drive, which is tidy for about a week.\n\nThe kickoff defaults to the third business day after signing, at 10am. Change `kickoff_offset_days` in **Configuration** if that is too soon.",
        "height": 340,
        "width": 340,
        "color": 5
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000003",
      "name": "Sticky Note - Drive",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [740, -480]
    },
    {
      "parameters": {
        "formTitle": "New client signed",
        "formDescription": "Fill this in once the paperwork is done. Everything else happens on its own.",
        "formFields": {
          "values": [
            { "fieldLabel": "client_name", "requiredField": true },
            { "fieldLabel": "contact_name", "requiredField": true },
            { "fieldLabel": "contact_email", "fieldType": "email", "requiredField": true },
            { "fieldLabel": "project_name", "requiredField": true },
            { "fieldLabel": "value", "fieldType": "text", "requiredField": false },
            { "fieldLabel": "owner_email", "fieldType": "email", "requiredField": true },
            { "fieldLabel": "notes", "fieldType": "textarea", "requiredField": false }
          ]
        },
        "options": {}
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000010",
      "name": "Client Signed",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [-160, 0],
      "webhookId": "e5f6a7b8-0005-4000-8000-00000000f001"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            { "id": "cfg-off", "name": "kickoff_offset_days", "value": 3, "type": "number" },
            { "id": "cfg-hour", "name": "kickoff_hour", "value": 10, "type": "number" },
            { "id": "cfg-mins", "name": "kickoff_minutes", "value": 45, "type": "number" },
            { "id": "cfg-chan", "name": "team_channel", "value": "#clients", "type": "string" },
            { "id": "cfg-org", "name": "organisation_name", "value": "Better Automations", "type": "string" }
          ]
        },
        "options": {}
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000011",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [60, 0]
    },
    {
      "parameters": {
        "jsCode": "// Every name, date and identifier is built once, here. Nothing downstream invents its own.\nconst cfg = $('Configuration').first().json;\n\nconst slug = (s) => String(s ?? '')\n  .toLowerCase()\n  .replace(/\\b(pty|ltd|limited|inc|llc|group|holdings)\\b/g, '')\n  .replace(/[^a-z0-9]+/g, '-')\n  .replace(/^-+|-+$/g, '')\n  .slice(0, 40);\n\n// Business days, so a Friday signing does not book a kickoff on Sunday.\nconst addBusinessDays = (from, n) => {\n  const d = new Date(from);\n  let added = 0;\n  while (added < n) {\n    d.setDate(d.getDate() + 1);\n    const day = d.getDay();\n    if (day !== 0 && day !== 6) added++;\n  }\n  return d;\n};\n\nreturn $input.all().map((item) => {\n  const c = item.json;\n  const now = new Date();\n  const year = now.getFullYear();\n\n  const kickoff = addBusinessDays(now, cfg.kickoff_offset_days);\n  kickoff.setHours(cfg.kickoff_hour, 0, 0, 0);\n  const kickoffEnd = new Date(kickoff.getTime() + cfg.kickoff_minutes * 60 * 1000);\n\n  const clientSlug = slug(c.client_name);\n\n  return {\n    json: {\n      ...c,\n      client_slug: clientSlug,\n      client_code: `${clientSlug}-${year}`.toUpperCase(),\n      folder_name: `${c.client_name} (${year})`,\n      kickoff_start: kickoff.toISOString(),\n      kickoff_end: kickoffEnd.toISOString(),\n      kickoff_readable: kickoff.toLocaleString('en-AU', {\n        weekday: 'long', day: 'numeric', month: 'long', hour: 'numeric', minute: '2-digit', hour12: true,\n      }),\n      signed_date: now.toISOString().slice(0, 10),\n    },\n  };\n});"
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000012",
      "name": "Derive Everything",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [320, 0]
    },
    {
      "parameters": {
        "resource": "folder",
        "name": "={{ $json.folder_name }}",
        "driveId": { "__rl": true, "value": "My Drive", "mode": "list", "cachedResultName": "My Drive" },
        "folderId": { "__rl": true, "value": "root", "mode": "list", "cachedResultName": "/ (Root folder)" },
        "options": {}
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000013",
      "name": "Create Client Folder",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [580, 0]
    },
    {
      "parameters": {
        "calendar": { "__rl": true, "value": "", "mode": "list" },
        "start": "={{ $('Derive Everything').item.json.kickoff_start }}",
        "end": "={{ $('Derive Everything').item.json.kickoff_end }}",
        "additionalFields": {
          "summary": "=Kickoff: {{ $('Derive Everything').item.json.project_name }} ({{ $('Derive Everything').item.json.client_name }})",
          "description": "=Kickoff call for {{ $('Derive Everything').item.json.project_name }}.\n\nClient folder: {{ $json.webViewLink }}\n\nNotes from signing:\n{{ $('Derive Everything').item.json.notes || 'None recorded.' }}",
          "attendees": ["={{ $('Derive Everything').item.json.contact_email }}", "={{ $('Derive Everything').item.json.owner_email }}"],
          "sendUpdates": "all"
        }
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000014",
      "name": "Book the Kickoff",
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [840, 0]
    },
    {
      "parameters": {
        "sendTo": "={{ $('Derive Everything').item.json.contact_email }}",
        "subject": "=Welcome aboard, {{ $('Derive Everything').item.json.client_name }}",
        "emailType": "text",
        "message": "=Hi {{ $('Derive Everything').item.json.contact_name.split(' ')[0] }},\n\nGood to have you on board. Here is what happens next.\n\nYour kickoff call is booked for {{ $('Derive Everything').item.json.kickoff_readable }}, and a calendar invite is already on its way. It runs about {{ $('Configuration').first().json.kickoff_minutes }} minutes and we use it to walk through the process properly before anything gets built.\n\nEverything for {{ $('Derive Everything').item.json.project_name }} lives in one shared folder:\n{{ $('Create Client Folder').item.json.webViewLink }}\n\nIf anything needs to change before then, just reply to this email.\n\n{{ $('Configuration').first().json.organisation_name }}",
        "options": { "replyTo": "={{ $('Derive Everything').item.json.owner_email }}" }
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000015",
      "name": "Send the Welcome Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [1100, 0],
      "webhookId": "e5f6a7b8-0005-4000-8000-00000000f002"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": { "__rl": true, "value": "", "mode": "list", "cachedResultName": "Client Register" },
        "sheetName": { "__rl": true, "value": "", "mode": "list", "cachedResultName": "Clients" },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "client_code": "={{ $('Derive Everything').item.json.client_code }}",
            "client_name": "={{ $('Derive Everything').item.json.client_name }}",
            "project_name": "={{ $('Derive Everything').item.json.project_name }}",
            "contact_name": "={{ $('Derive Everything').item.json.contact_name }}",
            "contact_email": "={{ $('Derive Everything').item.json.contact_email }}",
            "owner_email": "={{ $('Derive Everything').item.json.owner_email }}",
            "value": "={{ $('Derive Everything').item.json.value }}",
            "signed_date": "={{ $('Derive Everything').item.json.signed_date }}",
            "folder_link": "={{ $('Create Client Folder').item.json.webViewLink }}",
            "status": "onboarding"
          }
        },
        "options": {}
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000016",
      "name": "Add to Client Register",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [1360, 0]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": { "__rl": true, "value": "={{ $('Configuration').first().json.team_channel }}", "mode": "name" },
        "text": "=:tada: *New client: {{ $('Derive Everything').item.json.client_name }}*\n\n*Project:* {{ $('Derive Everything').item.json.project_name }}\n*Contact:* {{ $('Derive Everything').item.json.contact_name }} · {{ $('Derive Everything').item.json.contact_email }}\n*Owner:* {{ $('Derive Everything').item.json.owner_email }}\n{{ $('Derive Everything').item.json.value ? '*Value:* ' + $('Derive Everything').item.json.value : '' }}\n\n*Kickoff:* {{ $('Derive Everything').item.json.kickoff_readable }}\n*Folder:* {{ $('Create Client Folder').item.json.webViewLink }}\n\nFolder made, kickoff booked, welcome email sent, register updated.",
        "otherOptions": { "includeLinkToWorkflow": false }
      },
      "id": "e5f6a7b8-0005-4000-8000-000000000017",
      "name": "Tell the Team",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [1620, 0],
      "webhookId": "e5f6a7b8-0005-4000-8000-00000000f003"
    }
  ],
  "connections": {
    "Client Signed": { "main": [[{ "node": "Configuration", "type": "main", "index": 0 }]] },
    "Configuration": { "main": [[{ "node": "Derive Everything", "type": "main", "index": 0 }]] },
    "Derive Everything": { "main": [[{ "node": "Create Client Folder", "type": "main", "index": 0 }]] },
    "Create Client Folder": { "main": [[{ "node": "Book the Kickoff", "type": "main", "index": 0 }]] },
    "Book the Kickoff": { "main": [[{ "node": "Send the Welcome Email", "type": "main", "index": 0 }]] },
    "Send the Welcome Email": { "main": [[{ "node": "Add to Client Register", "type": "main", "index": 0 }]] },
    "Add to Client Register": { "main": [[{ "node": "Tell the Team", "type": "main", "index": 0 }]] }
  },
  "settings": { "executionOrder": "v1" },
  "pinData": {},
  "meta": {},
  "tags": []
}
