{
  "id": "93WJ5MeVEIgDObFL",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Birthday Multi-Channel Notifier",
  "tags": [
    {
      "id": "BdBRfTYYmsE2WeD8",
      "name": "Published",
      "createdAt": "2026-06-15T07:16:02.335Z",
      "updatedAt": "2026-06-15T07:16:02.335Z"
    }
  ],
  "nodes": [
    {
      "id": "a790f342-ddfe-460a-900f-1a373d28360a",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1776,
        3120
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "621d5711-a02a-475b-a56e-83b80f7e65e1",
      "name": "Google Contacts",
      "type": "n8n-nodes-base.googleContacts",
      "notes": "Fetch contacts with names, birthdays, emails, phone numbers, and biographies. Biography can hold a Telegram chat ID if you choose that workaround.",
      "position": [
        2048,
        3120
      ],
      "parameters": {
        "fields": [
          "emailAddresses",
          "birthdays",
          "names",
          "phoneNumbers",
          "biographies"
        ],
        "options": {},
        "operation": "getAll",
        "returnAll": true
      },
      "notesInFlow": true,
      "typeVersion": 1
    },
    {
      "id": "26459c96-a5c5-44d6-95ca-e0b2ee3a6cc0",
      "name": "If WhatsApp",
      "type": "n8n-nodes-base.if",
      "notes": "Routes only contacts with a normalized phone number that fits WhatsApp validation.",
      "position": [
        3216,
        3200
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.hasWhatsapp }}",
              "rightValue": true
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.2
    },
    {
      "id": "ad08b9db-da11-4dc6-a758-dfd1dcbbfff3",
      "name": "WhatsApp Skipped",
      "type": "n8n-nodes-base.set",
      "notes": "Sets a readable status when a WhatsApp-safe phone number is not available.",
      "position": [
        3568,
        3296
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "wa_status_false",
              "name": "whatsappStatus",
              "type": "string",
              "value": "Skipped - no valid WhatsApp number"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "557cbebf-6243-4295-9ab0-57b6c8cbf455",
      "name": "If Telegram",
      "type": "n8n-nodes-base.if",
      "notes": "Routes only contacts with a numeric Telegram chat ID.",
      "position": [
        3200,
        3680
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.hasTelegram }}",
              "rightValue": true
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.2
    },
    {
      "id": "1c59c56e-0f66-40ba-87e9-b6a8e1a74c34",
      "name": "Telegram Skipped",
      "type": "n8n-nodes-base.set",
      "notes": "Sets a readable status when Telegram delivery is not possible.",
      "position": [
        3552,
        3776
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "tg_status_false",
              "name": "telegramStatus",
              "type": "string",
              "value": "Skipped - no valid Telegram chat ID"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "51808cbd-05b2-4f5f-a2a3-76219f15bae6",
      "name": "Sticky Input",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1648,
        2864
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 508,
        "content": "## Input stage\n- **Schedule Trigger** runs daily at 8 AM.\n- **Google Contacts** fetches birthdays, names, emails, phone numbers, and biographies.\n- Biography can be used as a temporary place to store a Telegram chat ID."
      },
      "typeVersion": 1
    },
    {
      "id": "11bb4274-1943-462c-b76b-2d2e84a5ef32",
      "name": "Sticky Validation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2016,
        2928
      ],
      "parameters": {
        "color": 7,
        "width": 476,
        "height": 444,
        "content": "## Validation stage\n**Normalize + Validate** cleans names, extracts birthday month/day, validates email, normalizes phone numbers for WhatsApp, and checks whether Telegram has a numeric chat ID. Only contacts with birthdays today continue."
      },
      "typeVersion": 1
    },
    {
      "id": "7fc04b3e-b343-4c9d-80aa-96dce33b1963",
      "name": "Sticky AI",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2560,
        2928
      ],
      "parameters": {
        "color": 7,
        "width": 456,
        "height": 440,
        "content": "## AI stage\n**AI Birthday Message** should return JSON with `subject`, `emailMessage`, and `shortMessage`. **Prepare Messages** adds safe fallback copy if the model returns incomplete data."
      },
      "typeVersion": 1
    },
    {
      "id": "8376f7bb-f954-4e6f-b2f1-6c3181bf49ec",
      "name": "Sticky Email",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3136,
        2496
      ],
      "parameters": {
        "color": 7,
        "width": 872,
        "height": 484,
        "content": "## Email branch\nSend through Gmail only when `hasEmail = true`. Sent and skipped states are both written back so the final summary stays readable."
      },
      "typeVersion": 1
    },
    {
      "id": "1ae7ff0a-00cd-4a2d-81bc-fe9997bf2d76",
      "name": "Sticky WhatsApp",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3136,
        3008
      ],
      "parameters": {
        "color": 7,
        "width": 868,
        "height": 456,
        "content": "## WhatsApp branch\nSend through WhatsApp Business Cloud only when the phone number passes validation. Replace `YOUR_WHATSAPP_PHONE_NUMBER_ID` and attach WhatsApp credentials before activation."
      },
      "typeVersion": 1
    },
    {
      "id": "57d59661-83d4-427d-8b92-e6f502e1f89d",
      "name": "Sticky Telegram",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3152,
        3520
      ],
      "parameters": {
        "color": 7,
        "width": 852,
        "height": 504,
        "content": "## Telegram branch\nTelegram delivery needs a real numeric `chatId` already known to your bot. If you only have a username or phone number, this branch is not reliable enough for production."
      },
      "typeVersion": 1
    },
    {
      "id": "6ad2d7da-3de8-4579-a1dd-f4e0ff4a0245",
      "name": "Sticky Summary",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4144,
        2768
      ],
      "parameters": {
        "color": 7,
        "width": 1116,
        "height": 756,
        "content": "## Final reporting\nThe merge nodes recombine all channel branches. **Build Summary** formats one Slack message per birthday contact, and **Slack Final Notification** posts the final operational result."
      },
      "typeVersion": 1
    },
    {
      "id": "99379c2c-d59b-4c17-af4e-7590654647e6",
      "name": "Sticky Checklist",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        2896
      ],
      "parameters": {
        "color": 4,
        "width": 540,
        "height": 332,
        "content": "# Birthday reminders\nRuns every morning, pulls Google Contacts, filters contacts whose birthday is today, generates a personalized message, sends it through available channels, and posts a final Slack summary.\n\n## Approval checklist\n- Add Slack channel ID.\n- Add WhatsApp phone number ID.\n- Confirm OpenAI credentials and prompt output.\n- Confirm Google Contacts exposes `biographies` if you use it for Telegram IDs.\n- Test each branch manually before enabling the schedule."
      },
      "typeVersion": 1
    },
    {
      "id": "006d5ab9-12ec-42ac-8c50-082d1271e618",
      "name": "Normalize + Validate1",
      "type": "n8n-nodes-base.code",
      "notes": "Normalizes contact data, validates channel readiness, and keeps only contacts whose birthday matches today.",
      "position": [
        2320,
        3120
      ],
      "parameters": {
        "jsCode": "const now = new Date();\nconst todayMonth = now.getMonth() + 1;\nconst todayDay = now.getDate();\n\nfunction getFirst(arr) {\n  return Array.isArray(arr) && arr.length ? arr[0] : null;\n}\n\nfunction cleanPhone(value) {\n  if (!value) return '';\n  const cleaned = String(value).replace(/[^\\d+]/g, '');\n  if (cleaned.startsWith('00')) return '+' + cleaned.slice(2);\n  return cleaned;\n}\n\nfunction parseBirthday(birthdays) {\n  if (!Array.isArray(birthdays) || birthdays.length === 0) return { month: null, day: null };\n  const b = birthdays[0] || {};\n  const d = b.date || {};\n  return {\n    month: Number(d.month || b.month || 0) || null,\n    day: Number(d.day || b.day || 0) || null,\n  };\n}\n\nreturn items.map((item) => {\n  const j = item.json;\n  const name = getFirst(j.names) || {};\n  const emailObj = getFirst(j.emailAddresses) || {};\n  const phoneObj = getFirst(j.phoneNumbers) || {};\n  const birthday = parseBirthday(j.birthdays);\n\n  const firstName = name.givenName || name.displayName || '';\n  const lastName = name.familyName || '';\n  const fullName = [firstName, lastName].filter(Boolean).join(' ').trim() || 'Friend';\n  const email = (emailObj.value || '').trim();\n  const whatsappNumber = cleanPhone(phoneObj.value || '');\n\n  const telegramChatId = (j.biographies && j.biographies[0] && j.biographies[0].value)\n    ? String(j.biographies[0].value).trim()\n    : '';\n\n  const hasBirthdayToday = birthday.month === todayMonth && birthday.day === todayDay;\n  const hasEmail = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);\n  const hasWhatsapp = /^\\+?[1-9]\\d{7,14}$/.test(whatsappNumber);\n  const hasTelegram = /^-?\\d+$/.test(telegramChatId);\n\n  return {\n    json: {\n      ...j,\n      firstName,\n      lastName,\n      fullName,\n      email,\n      whatsappNumber,\n      telegramChatId,\n      birthdayMonth: birthday.month,\n      birthdayDay: birthday.day,\n      hasBirthdayToday,\n      hasEmail,\n      hasWhatsapp,\n      hasTelegram,\n    }\n  };\n}).filter(item => item.json.hasBirthdayToday);"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "b2ca5f29-9d25-487f-80a3-c7a8055c8bf0",
      "name": "AI Birthday Message1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "notes": "Generates one reusable birthday message object for email and chat channels.",
      "position": [
        2624,
        3120
      ],
      "parameters": {
        "resource": "chat"
      },
      "notesInFlow": true,
      "typeVersion": 1.8
    },
    {
      "id": "bb6700e9-c125-4b78-93b0-3303d5062112",
      "name": "Prepare Messages1",
      "type": "n8n-nodes-base.code",
      "notes": "Applies fallback message values if the AI response is empty or incomplete.",
      "position": [
        2864,
        3120
      ],
      "parameters": {
        "jsCode": "const src = $input.first().json;\nconst subject = src.subject || `Happy Birthday ${src.firstName || src.fullName || ''}`.trim();\nconst emailMessage = src.emailMessage || `Wishing you a wonderful birthday, ${src.firstName || src.fullName || 'Friend'}! Have a fantastic day ahead.`;\nconst shortMessage = src.shortMessage || `Happy Birthday ${src.firstName || ''}! Wishing you a fantastic day!`.trim();\n\nreturn [{\n  json: {\n    ...src,\n    subject,\n    emailMessage,\n    shortMessage\n  }\n}];"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "80d7fc84-ce76-47c8-b8dd-e479ae96dd60",
      "name": "If Email1",
      "type": "n8n-nodes-base.if",
      "notes": "Routes only contacts with a validated email address to Gmail.",
      "position": [
        3184,
        2720
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.hasEmail }}",
              "rightValue": true
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.2
    },
    {
      "id": "b97e6227-2717-475f-9837-f68d89bfe1e6",
      "name": "Gmail1",
      "type": "n8n-nodes-base.gmail",
      "notes": "Sends the personalized birthday email only when email validation passed.",
      "position": [
        3536,
        2624
      ],
      "parameters": {
        "sendTo": "={{ $json.email }}",
        "message": "={{ $json.emailMessage }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $json.subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.1
    },
    {
      "id": "9c3545b3-d728-47c9-95fa-78fe8e1163dc",
      "name": "Email Skipped1",
      "type": "n8n-nodes-base.set",
      "notes": "Sets a readable status when email is unavailable or invalid.",
      "position": [
        3536,
        2816
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "email_status_false",
              "name": "emailStatus",
              "type": "string",
              "value": "Skipped - no valid email"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "bbc188bb-c70d-46be-aa4f-acd32dc3fa8f",
      "name": "Email Sent1",
      "type": "n8n-nodes-base.set",
      "notes": "Marks the email delivery path as sent for the final summary.",
      "position": [
        3776,
        2624
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "email_status_true",
              "name": "emailStatus",
              "type": "string",
              "value": "Sent"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "d830f644-74d8-4b3b-8ba4-c625f78d63eb",
      "name": "WhatsApp1",
      "type": "n8n-nodes-base.whatsApp",
      "notes": "Sends a short birthday message through WhatsApp Business Cloud. Replace the phoneNumberId and attach valid credentials.",
      "position": [
        3568,
        3136
      ],
      "parameters": {
        "textBody": "={{ $json.shortMessage }}",
        "operation": "send",
        "phoneNumberId": "YOUR_WHATSAPP_PHONE_NUMBER_ID",
        "additionalFields": {
          "previewUrl": false
        },
        "recipientPhoneNumber": "={{ $json.whatsappNumber }}"
      },
      "notesInFlow": true,
      "typeVersion": 1
    },
    {
      "id": "e9dc6509-d4cb-4b26-b973-9514c337fde4",
      "name": "WhatsApp Sent1",
      "type": "n8n-nodes-base.set",
      "notes": "Marks the WhatsApp delivery path as sent for the final summary.",
      "position": [
        3808,
        3136
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "wa_status_true",
              "name": "whatsappStatus",
              "type": "string",
              "value": "Sent"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "c2cc1606-caa2-4ad9-8db8-d320b5fd1479",
      "name": "Telegram1",
      "type": "n8n-nodes-base.telegram",
      "notes": "Sends a Telegram birthday message. This will fail without a valid bot-accessible chat ID.",
      "position": [
        3552,
        3616
      ],
      "parameters": {
        "text": "={{ $json.shortMessage }}",
        "chatId": "={{ $json.telegramChatId }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 1.2
    },
    {
      "id": "f7ddaf08-c5b7-4c1f-bb70-fb06f5e59105",
      "name": "Telegram Sent1",
      "type": "n8n-nodes-base.set",
      "notes": "Marks the Telegram delivery path as sent for the final summary.",
      "position": [
        3792,
        3616
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "tg_status_true",
              "name": "telegramStatus",
              "type": "string",
              "value": "Sent"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "9e70ff45-0748-48bd-9721-b690072ddba6",
      "name": "Merge Email1",
      "type": "n8n-nodes-base.merge",
      "notes": "Merges the sent and skipped email branches back into one item stream.",
      "position": [
        4160,
        2976
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "notesInFlow": true,
      "typeVersion": 3.2
    },
    {
      "id": "0bd03599-3ec6-4ad3-abc3-e41e1fd03f01",
      "name": "Merge WhatsApp1",
      "type": "n8n-nodes-base.merge",
      "notes": "Combines the email result with the WhatsApp sent or skipped branch.",
      "position": [
        4400,
        3152
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "notesInFlow": true,
      "typeVersion": 3.2
    },
    {
      "id": "9ed1abdb-e8b1-4ac0-988d-cfc46ed765cf",
      "name": "Merge Telegram1",
      "type": "n8n-nodes-base.merge",
      "notes": "Combines prior channel results with the Telegram sent or skipped branch.",
      "position": [
        4640,
        3312
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "notesInFlow": true,
      "typeVersion": 3.2
    },
    {
      "id": "811b9cd7-4ea3-42f6-b781-4f683cacccbd",
      "name": "Build Summary1",
      "type": "n8n-nodes-base.code",
      "notes": "Builds the final Slack-friendly summary showing sent or skipped status per channel.",
      "position": [
        4880,
        3312
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const j = item.json;\n  const lines = [\n    '\ud83c\udf82 Birthday automation completed',\n    '',\n    `Contact: ${j.fullName}`,\n    `Email: ${j.emailStatus || 'Unknown'}`,\n    `WhatsApp: ${j.whatsappStatus || 'Unknown'}`,\n    `Telegram: ${j.telegramStatus || 'Unknown'}`\n  ];\n\n  return {\n    json: {\n      ...j,\n      slackText: lines.join('\\n')\n    }\n  };\n});"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "4c391b1f-36d0-4048-8323-06e677c350c1",
      "name": "Slack Final Notification1",
      "type": "n8n-nodes-base.slack",
      "notes": "Posts the final ops summary to Slack after all channel branches finish.",
      "position": [
        5120,
        3312
      ],
      "parameters": {
        "text": "={{ $json.slackText }}",
        "select": "channel",
        "channelId": "YOUR_SLACK_CHANNEL_ID",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "notesInFlow": true,
      "typeVersion": 2.3
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "e895e0dd-d027-4dab-b1ff-b6079c6cd13e",
  "nodeGroups": [],
  "connections": {
    "Gmail1": {
      "main": [
        [
          {
            "node": "Email Sent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Email1": {
      "main": [
        [
          {
            "node": "Gmail1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Email Skipped1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram1": {
      "main": [
        [
          {
            "node": "Telegram Sent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WhatsApp1": {
      "main": [
        [
          {
            "node": "WhatsApp Sent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Sent1": {
      "main": [
        [
          {
            "node": "Merge Email1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Telegram": {
      "main": [
        [
          {
            "node": "Telegram1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Telegram Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If WhatsApp": {
      "main": [
        [
          {
            "node": "WhatsApp1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "WhatsApp Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Email1": {
      "main": [
        [
          {
            "node": "Merge WhatsApp1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Summary1": {
      "main": [
        [
          {
            "node": "Slack Final Notification1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Skipped1": {
      "main": [
        [
          {
            "node": "Merge Email1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Telegram Sent1": {
      "main": [
        [
          {
            "node": "Merge Telegram1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "WhatsApp Sent1": {
      "main": [
        [
          {
            "node": "Merge WhatsApp1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Google Contacts": {
      "main": [
        [
          {
            "node": "Normalize + Validate1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Telegram1": {
      "main": [
        [
          {
            "node": "Build Summary1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge WhatsApp1": {
      "main": [
        [
          {
            "node": "Merge Telegram1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Google Contacts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Skipped": {
      "main": [
        [
          {
            "node": "Merge Telegram1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "WhatsApp Skipped": {
      "main": [
        [
          {
            "node": "Merge WhatsApp1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Prepare Messages1": {
      "main": [
        [
          {
            "node": "If Email1",
            "type": "main",
            "index": 0
          },
          {
            "node": "If WhatsApp",
            "type": "main",
            "index": 0
          },
          {
            "node": "If Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Birthday Message1": {
      "main": [
        [
          {
            "node": "Prepare Messages1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize + Validate1": {
      "main": [
        [
          {
            "node": "AI Birthday Message1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}