{
  "name": "LGM 08 — Customer Event Dispatcher (SIMULATED)",
  "nodes": [
    {
      "parameters": {},
      "id": "f08-start",
      "name": "Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        120,
        480
      ]
    },
    {
      "parameters": {
        "jsCode": "// Manual-run fixture. Edit branch_hint/event_type here to exercise\n// different downstream paths when you click Execute workflow.\nconst branchHint = 'enquiry.received';\nconst fixture = {\n  event_id: 'EVT-' + Math.floor(Math.random() * 90000 + 10000),\n  event_type: branchHint,\n  received_at: new Date().toISOString(),\n  contact: {\n    name: 'Maya Reeve',\n    phone: '+61400000111',\n    email: 'maya@example.invalid',\n    marketing_consent: true\n  },\n  payload: { suburb: 'Cranbourne', service_type: 'earthworks' },\n  source: 'manual-test'\n};\nreturn [{ json: fixture }];"
      },
      "id": "f08-manual-fixture",
      "name": "Build Manual Fixture",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        340,
        480
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "lgm-dispatcher",
        "responseMode": "lastNode",
        "options": {}
      },
      "id": "f08-webhook",
      "name": "Dispatcher Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        120,
        320
      ],
      "webhookId": "lgm-dispatcher"
    },
    {
      "parameters": {
        "jsCode": "// Reusable dispatcher header. Responsibilities:\n//   1. Signature verification stub: real HMAC verification must read the\n//      raw request body (per your provider's signature scheme) BEFORE\n//      any other processing. The current stub does not check anything\n//      — it is a documented placeholder, not a working verifier.\n//   2. Idempotency stub: events with a previously-seen event_id must be\n//      dropped. Maintain durable idempotency state (Postgres / KV) —\n//      providers do NOT silently dedupe for you.\n//   3. Business-hours stub: not enforced here. See the production\n//      pattern described in docs/automation-runtime.md.\n//   4. Opt-out gate: contacts with marketing_consent=false are\n//      acknowledged but not dispatched downstream.\n//   5. Repeat-event guard stub: same (contact_phone + event_type)\n//      within a configurable window is dropped to the duplicate\n//      channel — see Repeat Guard IF node.\nconst items = $input.all();\nconst raw = items[0].json || {};\n\nconst contact = raw.contact || {};\nconst result = {\n  ...raw,\n  received_at: new Date().toISOString(),\n  contact,\n  checks: {\n    signature: 'stub-not-implemented',\n    idempotency: 'stub-not-implemented',\n    business_hours: 'stub-not-implemented',\n    opt_out: contact.marketing_consent === false ? 'BLOCKED' : 'ok',\n    repeat_event: 'pending'\n  }\n};\n\n// Blank-input guard: refuse to dispatch an event with no contact and no\n// event_id. Returning an early ack stops the Switch downstream.\nconst blankInput = (!contact.phone && !contact.email) || !result.event_id;\n\nif (result.checks.opt_out === 'BLOCKED') {\n  return [{\n    json: {\n      ...result,\n      dispatch_status: 'blocked-opt-out',\n      simulatedOutputs: [\n        { type: 'heading', label: 'Dispatcher — opt-out gate' },\n        { type: 'note', label: 'Result',\n          text: `Contact ${contact.name || '(unknown)'} has marketing_consent=false. Event acknowledged but no downstream calls will fire.` },\n        { type: 'code', label: 'Audit log', language: 'json', body: JSON.stringify(result, null, 2) }\n      ]\n    }\n  }];\n}\n\nif (blankInput) {\n  return [{\n    json: {\n      ...result,\n      dispatch_status: 'blocked-blank-input',\n      simulatedOutputs: [\n        { type: 'heading', label: 'Dispatcher — blank input' },\n        { type: 'note', label: 'Result',\n          text: 'Event has no event_id OR no contact phone/email. Returning 200 to stop retry storms; logged for human review.' },\n        { type: 'code', label: 'Audit log', language: 'json', body: JSON.stringify(result, null, 2) }\n      ]\n    }\n  }];\n}\n\nreturn [{ json: result }];"
      },
      "id": "f08-gate",
      "name": "Verify + Gate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        560,
        320
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "or",
          "conditions": [
            {
              "leftValue": "={{ $json.checks.repeat_event }}",
              "rightValue": "duplicate",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        }
      },
      "id": "f08-repeat",
      "name": "Repeat Event Guard",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        780,
        320
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "enquiry.received",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "enquiry",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "quote.sent",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "quote",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "job.completed",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "job",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "missed.call",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "missed",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "review.received",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "review",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "order.cod",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "cod",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "delivery.failed",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "ndr",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "booking.confirmed",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "booking",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "loyalty.enrol",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "loyalty",
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.event_type }}",
                    "rightValue": "winback.due",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "winback",
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "id": "f08-switch",
      "name": "Route by event_type",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        980,
        480
      ]
    },
    {
      "parameters": {
        "jsCode": "// Repeat-event drop: same contact + same event_type within the guard\n// window is logged and acknowledged without dispatch. The IF upstream\n// routes here when checks.repeat_event == 'duplicate'.\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Repeat-event guard fired' },\n  { type: 'note', label: 'Result',\n    text: `Duplicate (contact_phone + event_type) suppressed. Event acknowledged but no downstream call fired.` },\n  { type: 'code', label: 'Audit log', language: 'json', body: JSON.stringify($json, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'repeat-event', simulatedOutputs } }];"
      },
      "id": "f08-repeat-out",
      "name": "Build Repeat-Drop Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        980,
        200
      ]
    },
    {
      "parameters": {
        "jsCode": "// Dispatch Enquiry — replace with HTTP call / Execute Workflow when wiring real.\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — enquiry.received' },\n  { type: 'note', label: 'In production',\n    text: 'POST {downstream URL} with the original event body. Stamp Idempotency-Key: <event_id>. Maintain durable dedupe state yourself.' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'POST', url: 'https://n8n.example.invalid/webhook/lgm-01', body: $json, idempotency_key: $json.event_id }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'enquiry', simulatedOutputs } }];"
      },
      "id": "f08-enq",
      "name": "Dispatch Enquiry",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        80
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — quote.sent' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'POST', url: 'https://n8n.example.invalid/webhook/lgm-03', body: $json, idempotency_key: $json.event_id }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'quote', simulatedOutputs } }];"
      },
      "id": "f08-quote",
      "name": "Dispatch Quote",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        200
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — job.completed' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'POST', url: 'https://n8n.example.invalid/webhook/lgm-04', body: $json, idempotency_key: $json.event_id }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'job', simulatedOutputs } }];"
      },
      "id": "f08-job",
      "name": "Dispatch Job",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — missed.call' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'POST', url: 'https://n8n.example.invalid/webhook/lgm-02', body: $json, idempotency_key: $json.event_id }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'missed', simulatedOutputs } }];"
      },
      "id": "f08-missed",
      "name": "Dispatch Missed Call",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        440
      ]
    },
    {
      "parameters": {
        "jsCode": "// Review event: update the CRM record's review field; no outbound\n// message — handled by LGM-04 (review request).\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — review.received' },\n  { type: 'note', label: 'Pattern',\n    text: 'Tag the CRM record. No outbound review request from here; LGM-04 handles that with the same neutral link for everyone. Low-rating routing is enforced in LGM-04, not here.' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'PATCH', url: 'https://crm.example.invalid/contacts/' + ($json.payload?.contact_id || 'unknown'), body: { latest_review: $json.payload } }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'review', simulatedOutputs } }];"
      },
      "id": "f08-review",
      "name": "Dispatch Review",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        560
      ]
    },
    {
      "parameters": {
        "jsCode": "// COD confirmation — dispatch to a COD workflow (not currently a separate\n// template; this branch documents the contract).\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — order.cod' },\n  { type: 'note', label: 'Pattern',\n    text: 'Cash-on-delivery confirmation: SMS asks YES/NO. If no reply in window, escalate via dispatcher call. STOP keyword must be honoured by the SMS provider + CRM flag updated before any further sequence fires.' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'POST', url: 'https://n8n.example.invalid/webhook/lgm-cod', body: $json, idempotency_key: $json.event_id }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'cod', simulatedOutputs } }];"
      },
      "id": "f08-cod",
      "name": "Dispatch COD",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        680
      ]
    },
    {
      "parameters": {
        "jsCode": "// Delivery failed (NDR) — dispatch to reschedule workflow.\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — delivery.failed' },\n  { type: 'note', label: 'Pattern',\n    text: 'NDR reschedule: SMS self-serve on first attempt, agent call on second, deposit or release on third. Always check CRM attempt counter; do NOT just trust the inbound attempt field.' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'POST', url: 'https://n8n.example.invalid/webhook/lgm-ndr', body: $json, idempotency_key: $json.event_id }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'ndr', simulatedOutputs } }];"
      },
      "id": "f08-ndr",
      "name": "Dispatch NDR",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        800
      ]
    },
    {
      "parameters": {
        "jsCode": "// Booking confirmed — update CRM stage; no automatic campaign.\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — booking.confirmed' },\n  { type: 'note', label: 'Pattern',\n    text: 'CRM stage → Booked. Customer-selected slot is preserved from payload.slot (never auto-picked by this workflow).' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'PATCH', url: 'https://crm.example.invalid/contacts/' + ($json.contact?.contact_id || 'unknown'), body: { stage: 'Booked', slot: $json.payload?.slot || 'NOT_PROVIDED' } }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'booking', simulatedOutputs } }];"
      },
      "id": "f08-booking",
      "name": "Dispatch Booking",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        920
      ]
    },
    {
      "parameters": {
        "jsCode": "// Loyalty enrol — handover to enrolment welcome.\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — loyalty.enrol' },\n  { type: 'note', label: 'Pattern',\n    text: 'Enrolment confirmation only — no campaign auto-runs from a subscriber-list add. Configure the welcome campaign explicitly in the campaign tool.' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'POST', url: 'https://n8n.example.invalid/webhook/lgm-loyalty', body: $json, idempotency_key: $json.event_id }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'loyalty', simulatedOutputs } }];"
      },
      "id": "f08-loyalty",
      "name": "Dispatch Loyalty",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        1040
      ]
    },
    {
      "parameters": {
        "jsCode": "// Winback — dormant re-engagement. STOP/PAUSE keywords must be honoured.\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — winback.due' },\n  { type: 'note', label: 'Pattern',\n    text: 'Soft nudge first, then incentive. STOP / PAUSE keywords must be honoured by the SMS/email provider AND reflected as a CRM flag before any further sequence fires.' },\n  { type: 'code', label: 'Simulated dispatch',\n    language: 'json',\n    body: JSON.stringify({ method: 'POST', url: 'https://n8n.example.invalid/webhook/lgm-winback', body: $json, idempotency_key: $json.event_id }, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'winback', simulatedOutputs } }];"
      },
      "id": "f08-winback",
      "name": "Dispatch Winback",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        1160
      ]
    },
    {
      "parameters": {
        "jsCode": "// Catch-all for unhandled event_type.\nconst items = $input.all();\nconst $json = items[0].json;\nconst simulatedOutputs = [\n  { type: 'heading', label: '1. Dispatch — unhandled event_type' },\n  { type: 'note', label: 'Ack', text: `Unhandled event_type \"${$json.event_type}\"; logged for human review. Returning 200 to prevent retry storms.` },\n  { type: 'code', label: 'Raw event', language: 'json', body: JSON.stringify($json, null, 2) }\n];\nreturn [{ json: { ...$json, branch: 'unhandled', simulatedOutputs } }];"
      },
      "id": "f08-unhandled",
      "name": "Dispatch Unhandled",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        1280
      ]
    },
    {
      "parameters": {},
      "id": "f08-sink",
      "name": "Sink",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1440,
        600
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Build Manual Fixture",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Manual Fixture": {
      "main": [
        [
          {
            "node": "Verify + Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatcher Webhook": {
      "main": [
        [
          {
            "node": "Verify + Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify + Gate": {
      "main": [
        [
          {
            "node": "Repeat Event Guard",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Repeat Event Guard": {
      "main": [
        [
          {
            "node": "Build Repeat-Drop Output",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Route by event_type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by event_type": {
      "main": [
        [
          {
            "node": "Dispatch Enquiry",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch Quote",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch Job",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch Missed Call",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch Review",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch COD",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch NDR",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch Booking",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch Loyalty",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch Winback",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Dispatch Unhandled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Repeat-Drop Output": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Enquiry": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Quote": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Job": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Missed Call": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Review": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch COD": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch NDR": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Booking": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Loyalty": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Winback": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Unhandled": {
      "main": [
        [
          {
            "node": "Sink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner"
  },
  "versionId": "lgm-08-dispatcher",
  "id": "lgm-08-dispatcher",
  "meta": {
    "templateCredsSetupCompleted": false,
    "description": "REUSABLE dispatcher. Manual + Webhook entrypoints → signature/idempotency/business-hours STUBS → opt-out + repeat-event gates → Switch by event_type → simulated dispatch payloads. See docs/automation-runtime.md for the real HMAC + idempotency + business-hours hooks."
  },
  "tags": [
    {
      "name": "lgm-08-dispatcher"
    }
  ]
}
