{
  "name": "LGM 01 — Flagship Enquiry (SIMULATED)",
  "nodes": [
    {
      "parameters": {},
      "id": "f01-start",
      "name": "Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build a realistic fictional enquiry from the manual trigger.\n// Replace this with your real intake source (webhook / CRM / form) when\n// going live; the rest of the workflow does not depend on the trigger shape.\nconst fixture = {\n  enquiry_id: 'ENQ-' + Math.floor(Math.random() * 90000 + 10000),\n  received_at: new Date().toISOString(),\n  customer_name: 'Maya Reeve',\n  customer_phone: '+61400000111',\n  customer_email: 'maya@example.invalid',\n  suburb: 'Cranbourne',\n  service_type: 'front yard earthworks',\n  budget_aud: 6500,\n  budget_stated: true,\n  timing: 'next 2 weeks',\n  urgency: 'soon',\n  source: 'Website contact form',\n  consent_marketing: true,\n  consent_timestamp: new Date().toISOString(),\n  // Customer EXPLICITLY asked for a callback instead of auto-messaging.\n  customer_requested_call: false,\n  // If the customer has picked a slot, set this to the slot object.\n  // Build Qualified Output only emits a calendar entry when this is set —\n  // it never auto-picks a slot for the customer.\n  customer_selected_slot: null,\n  business: {\n    name: 'Bayside Earthworks',\n    timezone: 'Australia/Sydney',\n    service_radius_km: 25,\n    depot_suburb: 'Dandenong'\n  },\n  // Distance from depot (Cranbourne ≈ 18km from Dandenong).\n  distance_km: 18\n};\nreturn [{ json: fixture }];"
      },
      "id": "f01-fixture",
      "name": "Build Fixture",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Score the lead and decide the branch.\n// Pure function — no I/O. Mirrors the simulation function used by the website\n// /demos page so the demo page and the real n8n template share the same logic.\nconst items = $input.all();\nconst item = items[0];\nconst $json = item.json;\n\nfunction s(v) {\n  if (typeof v === 'string') return v;\n  if (typeof v === 'number') return String(v);\n  if (typeof v === 'boolean') return v ? 'yes' : 'no';\n  return '';\n}\n\nconst business = $json.business || {};\nconst radius = Number(business.service_radius_km || 25);\nconst distance = Number($json.distance_km || 0);\n\nlet score = 0;\nconst reasons = [];\n\n// Intent weight: explicit budget + timing signal.\nif ($json.budget_stated && Number($json.budget_aud) >= 2000) {\n  score += 35; reasons.push('budget-stated');\n}\nif (s($json.urgency).match(/immediate|asap|soon|urgent/i)) {\n  score += 25; reasons.push('urgency');\n}\nif (s($json.timing).match(/week|days/i)) {\n  score += 15; reasons.push('timing-this-month');\n}\nif (!$json.consent_marketing) {\n  score -= 50; reasons.push('no-marketing-consent');\n}\n\n// Service-area weight.\nconst inArea = distance > 0 && distance <= radius;\nif (!inArea) {\n  reasons.push('out-of-area');\n}\n\n// Branch resolution. Explicit customer-requested callback short-circuits\n// auto-acknowledgement: the customer has asked to talk to a human.\nlet branch;\nif ($json.customer_requested_call === true && inArea) {\n  branch = 'human-request';\n} else if (!$json.consent_marketing && !inArea) {\n  branch = 'referred-out';\n} else if (!inArea) {\n  branch = 'out-of-area';\n} else if (score >= 50) {\n  branch = 'qualified';\n} else if (score >= 20) {\n  branch = 'warm';\n} else {\n  branch = 'referred-out';\n}\n\nreturn [{\n  json: {\n    ...$json,\n    score,\n    reasons,\n    inArea,\n    branch\n  }\n}];"
      },
      "id": "f01-score",
      "name": "Score Lead",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.branch }}",
                    "rightValue": "qualified",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "qualified",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.branch }}",
                    "rightValue": "warm",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "warm",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.branch }}",
                    "rightValue": "human-request",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "human-request",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.branch }}",
                    "rightValue": "out-of-area",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "out-of-area",
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "id": "f01-switch",
      "name": "Route by Branch",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Qualified branch — simulate CRM record + acknowledgement + booking-if-selected +\n// business notification + follow-up sequence.\n// Customer-selected booking slot: we only emit a calendar entry when\n// payload.customer_selected_slot is set. Otherwise we offer the customer a\n// slot menu — never invent a confirmed appointment.\nconst items = $input.all();\nconst $json = items[0].json;\nconst business = $json.business || { name: 'Bayside Earthworks' };\nconst slot = $json.customer_selected_slot;\n\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. CRM record (SIMULATED Twenty)' },\n  { type: 'crm', label: 'Twenty CRM', rows: [\n    { label: 'Contact', value: `${$json.customer_name} <${$json.customer_email}>` },\n    { label: 'Phone', value: $json.customer_phone },\n    { label: 'Source', value: $json.source },\n    { label: 'Stage', value: 'New → Qualified' },\n    { label: 'Score', value: `${$json.score}/100 — reasons: ${($json.reasons || []).join(', ')}` },\n    { label: 'Service area', value: `${$json.inArea ? 'inside' : 'outside'} ${$json.distance_km} km` },\n    { label: 'Consent (marketing)', value: $json.consent_marketing ? 'opted-in' : 'no' },\n    { label: 'Customer-requested callback', value: $json.customer_requested_call ? 'yes' : 'no' }\n  ]},\n  { type: 'heading', label: '2. Customer acknowledgement (SIMULATED SMS)' },\n  { type: 'message', channel: 'sms', label: 'SMS via Chatwoot',\n    body: `Hi ${$json.customer_name}, thanks for the ${$json.service_type} enquiry in ${$json.suburb}. These are our next 3 site-visit windows — reply with the number you want and we will lock it in: (1) tomorrow 9–9:20am (2) Wed 11–11:20am (3) Thu 2–2:20pm. — ${business.name}` },\n  slot ? { type: 'heading', label: '3. Booking confirmation (SIMULATED calendar)' } : null,\n  slot ? { type: 'calendar', label: 'Calendar (customer-selected)', summary: `Site visit — ${$json.service_type}`, startsAt: slot.startsAt || 'unspecified', durationMin: slot.durationMin || 20, attendees: [$json.customer_name, 'Site supervisor'] } : null,\n  slot ? null : { type: 'heading', label: '3. Awaiting customer slot selection' },\n  slot ? null : { type: 'note', label: 'Booking policy', text: 'Customer has not picked a slot. No calendar entry created. Once the customer replies with a slot number, an external system (NOT this workflow) creates the booking.' },\n  { type: 'heading', label: '4. Business notification (SIMULATED internal)' },\n  { type: 'message', channel: 'internal', label: 'Slack/Teams webhook',\n    body: `New QUALIFIED lead from ${$json.source}: ${$json.customer_name}, ${$json.service_type} in ${$json.suburb}, budget $${$json.budget_aud}. Score ${$json.score}.${$json.customer_requested_call ? ' Customer asked for callback.' : ''}` },\n  { type: 'heading', label: '5. Follow-up sequence (SIMULATED Listmonk)' },\n  { type: 'message', channel: 'email', label: 'Listmonk — sequence day 0',\n    subject: `Site visit confirmation — ${$json.suburb}`,\n    body: `Hi ${$json.customer_name}, thanks for the detail on your ${$json.service_type}. Pick one of the next 3 site-visit windows and we will lock it in. — ${business.name}` },\n  { type: 'message', channel: 'email', label: 'Listmonk — sequence day 3',\n    subject: `Quick check-in — your ${$json.service_type} project`,\n    body: `Just checking you've got what you need from the visit notes. Reply any time. — ${business.name}` }\n].filter(Boolean);\n\nreturn [{ json: { ...$json, branch: 'qualified', simulatedOutputs } }];"
      },
      "id": "f01-qualified",
      "name": "Build Qualified Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1140,
        120
      ]
    },
    {
      "parameters": {
        "jsCode": "// Warm branch — not enough signal yet; ask qualifying questions before quoting.\nconst items = $input.all();\nconst $json = items[0].json;\nconst business = $json.business || { name: 'Bayside Earthworks' };\n\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. CRM record (SIMULATED Twenty)' },\n  { type: 'crm', label: 'Twenty CRM', rows: [\n    { label: 'Contact', value: `${$json.customer_name} <${$json.customer_email}>` },\n    { label: 'Stage', value: 'New → Warm (needs detail)' },\n    { label: 'Score', value: `${$json.score}/100 — reasons: ${($json.reasons || []).join(', ')}` },\n    { label: 'Next action', value: 'Send measured-quote intake email' }\n  ]},\n  { type: 'heading', label: '2. Customer intake email (SIMULATED email)' },\n  { type: 'message', channel: 'email', label: 'Listmonk', subject: `A few quick questions about your ${$json.service_type}`,\n    body: `Hi ${$json.customer_name}, to give you a useful quote could you let us know: area size, access notes (gate/slope/pets), preferred timing. I'll come back with availability. — ${business.name}` },\n  { type: 'heading', label: '3. Internal task (SIMULATED Todoist)' },\n  { type: 'task', label: 'Todoist', title: 'Send measured quote once details returned',\n    owner: 'Estimator', dueIn: 'Within 2 business days',\n    notes: `Lead ref: ${$json.enquiry_id}` },\n  { type: 'heading', label: '4. Business notification (SIMULATED internal)' },\n  { type: 'message', channel: 'internal', label: 'Slack/Teams webhook',\n    body: `Warm lead from ${$json.source}: ${$json.customer_name}, ${$json.service_type} in ${$json.suburb}. Score ${$json.score}. Awaiting scope detail.` }\n];\n\nreturn [{ json: { ...$json, branch: 'warm', simulatedOutputs } }];"
      },
      "id": "f01-warm",
      "name": "Build Warm Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1140,
        280
      ]
    },
    {
      "parameters": {
        "jsCode": "// Human-request branch — customer EXPLICITLY asked for a callback.\n// No automated SMS or email; instead a short ack promising a call, plus\n// an internal task assigned to a person. Booking is NOT auto-created —\n// the human takes it from here.\nconst items = $input.all();\nconst $json = items[0].json;\nconst business = $json.business || { name: 'Bayside Earthworks' };\n\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. CRM record (SIMULATED Twenty)' },\n  { type: 'crm', label: 'Twenty CRM', rows: [\n    { label: 'Contact', value: `${$json.customer_name} <${$json.customer_email}>` },\n    { label: 'Stage', value: 'New → Human callback requested' },\n    { label: 'Score', value: `${$json.score}/100` },\n    { label: 'Customer-requested callback', value: 'yes' }\n  ]},\n  { type: 'heading', label: '2. Customer ack (short — no auto booking)' },\n  { type: 'message', channel: 'sms', label: 'SMS via Chatwoot',\n    body: `Hi ${$json.customer_name}, got your note — we will call you back within 30 minutes during business hours. — ${business.name}` },\n  { type: 'heading', label: '3. Internal task (SIMULATED Todoist)' },\n  { type: 'task', label: 'Todoist',\n    title: `Call back ${$json.customer_name} (${$json.customer_phone})`,\n    owner: 'Office',\n    dueIn: 'Within 30 minutes',\n    notes: `Customer asked for a callback. Service: ${$json.service_type} in ${$json.suburb}. Score ${$json.score}.` },\n  { type: 'heading', label: '4. Business notification (SIMULATED internal)' },\n  { type: 'message', channel: 'internal', label: 'Slack/Teams webhook',\n    body: `HUMAN-REQUEST lead: ${$json.customer_name} (${$json.customer_phone}) — callback needed within 30 min. ${$json.service_type} in ${$json.suburb}.` },\n  { type: 'note', label: 'Booking policy',\n    text: 'No calendar entry created in this branch — the human takes the call and books manually. No automated sequence is sent.' }\n];\n\nreturn [{ json: { ...$json, branch: 'human-request', simulatedOutputs } }];"
      },
      "id": "f01-human",
      "name": "Build Human-Request Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1140,
        440
      ]
    },
    {
      "parameters": {
        "jsCode": "// Out-of-area branch — polite decline + recommendation path. No internal alert.\nconst items = $input.all();\nconst $json = items[0].json;\nconst business = $json.business || { name: 'Bayside Earthworks' };\n\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. CRM tag (SIMULATED Twenty)' },\n  { type: 'crm', label: 'Twenty CRM', rows: [\n    { label: 'Stage', value: 'Closed → Out-of-area' },\n    { label: 'Distance', value: `${$json.distance_km} km from depot` },\n    { label: 'Reason', value: 'Outside current service radius' }\n  ]},\n  { type: 'heading', label: '2. Customer reply (SIMULATED email)' },\n  { type: 'message', channel: 'email', label: 'Listmonk', subject: 'Sorry — outside our service area',\n    body: `Hi ${$json.customer_name}, ${$json.suburb} is further than our current run sheet. Two options: we can share recommendations in your area, or put you on our list if we open that zone later this year. — ${business.name}` },\n  { type: 'note', label: 'Sequence policy',\n    text: 'Tagged out-of-area. No follow-up beyond the courtesy reply.' }\n];\n\nreturn [{ json: { ...$json, branch: 'out-of-area', simulatedOutputs } }];"
      },
      "id": "f01-oos",
      "name": "Build Out-of-Area Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1140,
        600
      ]
    },
    {
      "parameters": {
        "jsCode": "// Fallback (referred-out / low signal): no CRM rules, single courtesy email.\nconst items = $input.all();\nconst $json = items[0].json;\nconst business = $json.business || { name: 'Bayside Earthworks' };\n\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. CRM tag (SIMULATED Twenty)' },\n  { type: 'crm', label: 'Twenty CRM', rows: [\n    { label: 'Stage', value: 'Closed → Referred out' },\n    { label: 'Score', value: `${$json.score}/100` }\n  ]},\n  { type: 'heading', label: '2. Customer reply (SIMULATED email)' },\n  { type: 'message', channel: 'email', label: 'Listmonk', subject: 'Thanks for your enquiry',\n    body: `Hi ${$json.customer_name}, thanks for getting in touch. We're not the right fit for the scope described — here are two businesses closer to your area. Best of luck. — ${business.name}` },\n  { type: 'note', label: 'Sequence policy',\n    text: 'Tagged referred-out. CRM rule: no further sequences.' }\n];\n\nreturn [{ json: { ...$json, branch: 'referred-out', simulatedOutputs } }];"
      },
      "id": "f01-ref",
      "name": "Build Referred-Out Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1140,
        760
      ]
    },
    {
      "parameters": {},
      "id": "f01-sink",
      "name": "Sink",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1380,
        440
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Build Fixture",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Fixture": {
      "main": [
        [
          {
            "node": "Score Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Score Lead": {
      "main": [
        [
          {
            "node": "Route by Branch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Branch": {
      "main": [
        [
          {
            "node": "Build Qualified Output",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Warm Output",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Human-Request Output",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Out-of-Area Output",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Referred-Out Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Qualified Output": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Warm Output": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Human-Request Output": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Out-of-Area Output": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Referred-Out Output": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner"
  },
  "versionId": "lgm-01-flagship-enquiry",
  "id": "lgm-01-flagship-enquiry",
  "meta": {
    "templateCredsSetupCompleted": false,
    "description": "SIMULATED pipeline. Includes human-request branch (customer explicitly asked for callback) and customer-selected booking slot (no auto-invented appointments). See docs/automation-runtime.md for the real-integration replacement guide."
  },
  "tags": [
    {
      "name": "lgm-01-flagship-enquiry"
    }
  ]
}
