{
  "name": "Reply to every new lead on WhatsApp within 60 seconds",
  "nodes": [
    {
      "parameters": {
        "content": "## Reply to every new lead on WhatsApp within 60 seconds\n\nWhat you get: a personalised WhatsApp message to every new CRM lead the moment it is created, from your own number — never twice to the same number, with a follow-up the next day if nobody answers, and a CRM task for a human when a message cannot be delivered.\nWhat it replaces: the \"we'll call you back\" email and the hour a lead waits while nobody notices it.\nSetup time: about 15 minutes.\n\nLeads answered in the first minute close more often — this template makes that automatic.\n\nWorks with a normal WhatsApp number — no Meta Business account needed.\n\nThree entry points live in this one workflow.\nA (top lane) — a lead is created in Bitrix24: the phone is normalised to E.164, numbers welcomed in the last few days are skipped, the opening line is chosen by lead source and the language by country code, and the message goes out. If it goes out, the lead gets a timeline note, moves to the Contacted stage and its owner gets a Bitrix24 notification. If it does not, the owner gets a task instead — no lead is silently lost.\nB (middle lane) — the lead answers on WhatsApp: the sender's number is matched to a lead sitting in the Contacted stage, which then moves to the Replied stage, gets the customer's words on its timeline, and its owner is notified.\nC (bottom lane) — an hourly sweep: welcomed over 24 hours ago and still silent gets one more message in the same language; still silent after 72 hours moves to the No-response stage. Nothing else is chased.\n\nWhat the workflow remembers, and where: one small record per phone number in this workflow's static data (number, lead id, language, when it was welcomed, whether it replied). It is not a database — deleting, duplicating or re-importing the workflow clears it, and the next lead from a known number is welcomed once more.\n\nAbout phone numbers: n8n's Code node has no libphonenumber, so the \"Normalise Phone and Detect Language\" node carries a short country-code table plus the default country code from Settings. Numbers written with a leading \"+\" or \"00\" are trusted as-is; a number that already starts with a known country code and is 11-15 digits long is treated as international; anything else is treated as a national number and gets the default country code. Add your own code to the table in that node if it is missing.\n\nStages: stageContacted defaults to IN_PROCESS, a stage every Bitrix24 portal has. stageReplied (UC_REPLIED) and stageNoResponse (UC_NO_RESPONSE) are placeholders — create those two lead statuses in Bitrix24 (CRM → Settings → Statuses → Lead statuses) and paste their real ids into the Settings nodes, or point them at statuses you already use.\n\nSetup:\n1. Install the community node: n8n Settings -> Community Nodes -> npm package `n8n-nodes-wasync`.\n2. Create a free WASync account at https://developers.wasync.app/register, connect a WhatsApp number, then create an API key at https://developers.wasync.app/keys.\n3. In n8n, add a new \"WASync API\" credential using that key and name it \"WASync account\".\n4. Fill in all three \"Settings\" nodes — one per entry point, and they must hold the same values. Never commit real portal URLs, tokens, connection ids or phone numbers.\n5. In Bitrix24, create an inbound webhook (Applications -> Webhooks -> Inbound) with scopes crm, tasks and im, and paste its portal URL and token into the Settings nodes. The workflow calls crm.lead.get, crm.lead.list, crm.lead.update, crm.timeline.comment.add, tasks.task.add and im.notify.system.add — the last two through Bitrix24's batch method, one HTTP call instead of three.\n6. In Bitrix24, add an outbound webhook (Applications -> Webhooks -> Outbound) for event ONCRMLEADADD pointing at the Production URL of the \"Receive New Lead from Bitrix24\" node.\n7. Point WASync at the \"Receive WhatsApp Reply\" node: copy its Production URL and register it at developers.wasync.app (Webhooks section) for event `message.received`.\n\nDocs: https://docs.wasync.app/docs/guides/n8n?utm_source=n8n&utm_medium=template&utm_campaign=t1",
        "height": 880,
        "width": 3160
      },
      "name": "Read Me First",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -140,
        -960
      ]
    },
    {
      "parameters": {
        "content": "**① Trigger — new lead**\nBitrix24 calls this webhook on ONCRMLEADADD.\nCopy the node's Production URL into Bitrix24 →\nApplications → Webhooks → Outbound.",
        "height": 1188,
        "width": 280,
        "color": 7
      },
      "name": "① Trigger — new lead",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -140,
        514
      ]
    },
    {
      "parameters": {
        "content": "**② Check and route the lead**\nReads the lead, normalises the phone to E.164, skips numbers that\nwere already welcomed inside the dedupe window, and picks a branch\nby lead source. A skipped lead gets a note on its timeline saying\nwhy, so nobody wonders where the message went.",
        "height": 1188,
        "width": 1240,
        "color": 7
      },
      "name": "② Check and route the lead",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        180,
        514
      ]
    },
    {
      "parameters": {
        "content": "**③ Personalise and send**\nEach lead source has its own opening line, in the language matched\nfrom the phone's country code. The message leaves from your own\nWhatsApp number.",
        "height": 1188,
        "width": 920,
        "color": 7
      },
      "name": "③ Personalise and send",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1460,
        514
      ]
    },
    {
      "parameters": {
        "content": "**④ Record the outcome**\nSent: timeline note, Contacted stage and a notification for the\nresponsible person — one Bitrix24 batch call.\nFailed: a task for the responsible person with the reason, so the\nlead is followed up by a human instead of being lost.",
        "height": 1188,
        "width": 600,
        "color": 7
      },
      "name": "④ Record the outcome",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2420,
        514
      ]
    },
    {
      "parameters": {
        "content": "**Settings — fill these in**\nPlaceholders only — replace every value here before you activate the\nworkflow. Nothing else in the workflow needs editing.\nFields: bitrixPortalUrl, bitrixWebhookToken, companyName, wasyncConnectionId, defaultCountryCode, dedupeDays, secondTouchAfterHours, noResponseAfterHours, stageContacted, stageReplied, stageNoResponse, sourceWeb, sourceCall, sourceAdvertising, fallbackResponsibleId, defaultLanguage, languageByPrefix, templates.",
        "height": 454,
        "width": 600,
        "color": 7
      },
      "name": "Settings — fill these in",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -140,
        0
      ]
    },
    {
      "parameters": {
        "content": "**① Trigger — the lead replies**\nWASync posts every inbound WhatsApp message here.\nRegister this node's Production URL as your webhook.",
        "height": 366,
        "width": 280,
        "color": 7
      },
      "name": "① Trigger — the lead replies",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -140,
        2336
      ]
    },
    {
      "parameters": {
        "content": "**② Match the reply to a lead**\nLooks for a lead whose phone ends with the sender's digits and that\nis still sitting in the Contacted stage. A stranger, or a lead that\nmoved on, is left alone.",
        "height": 366,
        "width": 600,
        "color": 7
      },
      "name": "② Match the reply to a lead",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        180,
        2336
      ]
    },
    {
      "parameters": {
        "content": "**③ Update the lead**\nMoves the lead to the Replied stage, puts the customer's own words\non the timeline and notifies the responsible person. The hourly\nsweep stops chasing this lead from here on.",
        "height": 366,
        "width": 600,
        "color": 7
      },
      "name": "③ Update the lead",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        820,
        2336
      ]
    },
    {
      "parameters": {
        "content": "**Settings — fill these in**\nPlaceholders only — replace every value here before you activate the\nworkflow. Nothing else in the workflow needs editing.\nFields: bitrixPortalUrl, bitrixWebhookToken, companyName, wasyncConnectionId, defaultCountryCode, dedupeDays, secondTouchAfterHours, noResponseAfterHours, stageContacted, stageReplied, stageNoResponse, sourceWeb, sourceCall, sourceAdvertising, fallbackResponsibleId, defaultLanguage, languageByPrefix, templates.",
        "height": 454,
        "width": 600,
        "color": 7
      },
      "name": "Settings — fill these in (2)",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -140,
        1822
      ]
    },
    {
      "parameters": {
        "content": "**① Trigger — hourly sweep**\nRuns every hour. Most hours there is nothing to do.",
        "height": 366,
        "width": 280,
        "color": 7
      },
      "name": "① Trigger — hourly sweep",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -140,
        3336
      ]
    },
    {
      "parameters": {
        "content": "**② Pick what is due**\nWelcomed a day ago and still silent → one more message.\nSilent past the give-up window → stop.\nOlder than the dedupe window → forgotten, so the number can be\nwelcomed again.",
        "height": 366,
        "width": 600,
        "color": 7
      },
      "name": "② Pick what is due",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        180,
        3336
      ]
    },
    {
      "parameters": {
        "content": "**③ Follow up or give up**\nA single second-touch message, in the lead's language, then a note\non the timeline. Leads that stayed silent get the No-response\nstage instead of another message.",
        "height": 366,
        "width": 600,
        "color": 7
      },
      "name": "③ Follow up or give up",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        820,
        3336
      ]
    },
    {
      "parameters": {
        "content": "**Settings — fill these in**\nPlaceholders only — replace every value here before you activate the\nworkflow. Nothing else in the workflow needs editing.\nFields: bitrixPortalUrl, bitrixWebhookToken, companyName, wasyncConnectionId, defaultCountryCode, dedupeDays, secondTouchAfterHours, noResponseAfterHours, stageContacted, stageReplied, stageNoResponse, sourceWeb, sourceCall, sourceAdvertising, fallbackResponsibleId, defaultLanguage, languageByPrefix, templates.",
        "height": 454,
        "width": 600,
        "color": 7
      },
      "name": "Settings — fill these in (3)",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -140,
        2822
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "wasync-t1-new-lead",
        "responseMode": "onReceived",
        "options": {}
      },
      "name": "Receive New Lead from Bitrix24",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        782
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "Settings-0",
              "name": "bitrixPortalUrl",
              "value": "https://yourportal.bitrix24.com",
              "type": "string"
            },
            {
              "id": "Settings-1",
              "name": "bitrixWebhookToken",
              "value": "REPLACE_WITH_YOUR_INBOUND_WEBHOOK_TOKEN",
              "type": "string"
            },
            {
              "id": "Settings-2",
              "name": "companyName",
              "value": "Your Company",
              "type": "string"
            },
            {
              "id": "Settings-3",
              "name": "wasyncConnectionId",
              "value": "REPLACE_WITH_YOUR_WASYNC_CONNECTION_ID",
              "type": "string"
            },
            {
              "id": "Settings-4",
              "name": "defaultCountryCode",
              "value": "40",
              "type": "string"
            },
            {
              "id": "Settings-5",
              "name": "dedupeDays",
              "value": 7,
              "type": "number"
            },
            {
              "id": "Settings-6",
              "name": "secondTouchAfterHours",
              "value": 24,
              "type": "number"
            },
            {
              "id": "Settings-7",
              "name": "noResponseAfterHours",
              "value": 72,
              "type": "number"
            },
            {
              "id": "Settings-8",
              "name": "stageContacted",
              "value": "IN_PROCESS",
              "type": "string"
            },
            {
              "id": "Settings-9",
              "name": "stageReplied",
              "value": "UC_REPLIED",
              "type": "string"
            },
            {
              "id": "Settings-10",
              "name": "stageNoResponse",
              "value": "UC_NO_RESPONSE",
              "type": "string"
            },
            {
              "id": "Settings-11",
              "name": "sourceWeb",
              "value": "WEB",
              "type": "string"
            },
            {
              "id": "Settings-12",
              "name": "sourceCall",
              "value": "CALLBACK",
              "type": "string"
            },
            {
              "id": "Settings-13",
              "name": "sourceAdvertising",
              "value": "ADVERTISING",
              "type": "string"
            },
            {
              "id": "Settings-14",
              "name": "fallbackResponsibleId",
              "value": "1",
              "type": "string"
            },
            {
              "id": "Settings-15",
              "name": "defaultLanguage",
              "value": "en",
              "type": "string"
            },
            {
              "id": "Settings-16",
              "name": "languageByPrefix",
              "value": "={{ {\"40\": \"ro\", \"966\": \"ar\", \"971\": \"ar\"} }}",
              "type": "object"
            },
            {
              "id": "Settings-17",
              "name": "templates",
              "value": "={{ {\"en.welcome_web\": \"Hi {name}, thanks for your enquiry on our website. This is {company} — you can reply right here on WhatsApp and we will get back to you within the hour.\", \"en.welcome_call\": \"Hi {name}, this is {company}. We received your call-back request. What is the best time to call you? You can also just reply here.\", \"en.welcome_ad\": \"Hi {name}, thanks for your interest in our offer. This is {company} — ask us anything here on WhatsApp and we will answer straight away.\", \"en.welcome_other\": \"Hi {name}, this is {company}. Thanks for getting in touch — how can we help?\", \"en.second_touch\": \"Hi {name}, just making sure our message reached you. {company} is still happy to help — reply here whenever it suits you.\", \"ro.welcome_web\": \"Bună {name}, mulțumim pentru mesajul trimis pe site. Suntem {company} — ne puteți răspunde direct aici, pe WhatsApp, și revenim în cel mult o oră.\", \"ro.welcome_call\": \"Bună {name}, aici {company}. Am primit solicitarea de a fi sunat. La ce oră vă putem apela? Ne puteți răspunde și aici.\", \"ro.welcome_ad\": \"Bună {name}, mulțumim pentru interesul față de oferta noastră. Suntem {company} — întrebați-ne orice aici, pe WhatsApp.\", \"ro.welcome_other\": \"Bună {name}, aici {company}. Mulțumim că ne-ați scris — cu ce vă putem ajuta?\", \"ro.second_touch\": \"Bună {name}, verificăm doar dacă v-a ajuns mesajul nostru. {company} vă stă la dispoziție — ne puteți scrie oricând.\", \"ar.welcome_web\": \"مرحباً {name}، شكراً لتواصلك عبر موقعنا. نحن {company} — يمكنك الرد هنا على واتساب وسنعود إليك خلال ساعة.\", \"ar.welcome_call\": \"مرحباً {name}، معك {company}. وصلنا طلب الاتصال بك. ما الوقت المناسب للاتصال؟ يمكنك أيضاً الرد هنا.\", \"ar.welcome_ad\": \"مرحباً {name}، شكراً لاهتمامك بعرضنا. نحن {company} — اسألنا عن أي شيء هنا على واتساب.\", \"ar.welcome_other\": \"مرحباً {name}، معك {company}. شكراً لتواصلك — كيف يمكننا مساعدتك؟\", \"ar.second_touch\": \"مرحباً {name}، نتأكد فقط من وصول رسالتنا إليك. {company} في خدمتك — يمكنك الرد في أي وقت.\"} }}",
              "type": "object"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "name": "Settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        0,
        334
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $('Settings').item.json.bitrixPortalUrl }}/rest/{{ $('Settings').item.json.bitrixWebhookToken }}/crm.lead.get",
        "options": {},
        "sendQuery": true,
        "specifyQuery": "keypair",
        "queryParameters": {
          "parameters": [
            {
              "name": "ID",
              "value": "={{ $('Receive New Lead from Bitrix24').item.json.body.data.FIELDS.ID }}"
            }
          ]
        }
      },
      "name": "Get Lead from Bitrix24",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        320,
        782
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Turn the new lead into one clean item: an E.164 phone number, a language, and a\n// decision (send / skip).\n//\n// n8n's Code sandbox has no libphonenumber, so the normaliser below is small and\n// deliberately predictable:\n//   * keep the digits only (the API wants digits, no \"+\");\n//   * a number written with a leading \"+\" or \"00\" is trusted as international;\n//   * a number that already starts with one of KNOWN_CODES *and* is 11-15 digits\n//     long is also treated as international (people often drop the \"+\");\n//   * anything else is treated as a national number: the trunk \"0\" is dropped and\n//     defaultCountryCode from Settings is put in front;\n//   * finally the length is checked against the E.164 range (8-15 digits).\n// Add your own country code to KNOWN_CODES if it is missing.\nconst s = $('Settings').first().json;\nconst lead = ($input.first().json || {}).result || {};\n\nconst KNOWN_CODES = ['1', '7', '20', '27', '30', '31', '33', '34', '36', '39', '40', '41',\n  '43', '44', '45', '46', '47', '48', '49', '52', '61', '62', '65', '81', '86', '90', '91',\n  '351', '352', '353', '358', '359', '370', '371', '372', '373', '380', '381', '385', '386',\n  '420', '421', '966', '971', '972', '974', '994', '995', '998'];\n\nfunction toE164(raw, defaultCode) {\n  const original = String(raw || '').trim();\n  const cc = String(defaultCode || '').replace(/\\D/g, '');\n  let d = original.replace(/\\D/g, '');\n  if (!d) return '';\n  let international = original.startsWith('+') || d.startsWith('00');\n  if (d.startsWith('00')) d = d.slice(2);\n  if (!international && d.length >= 11 && d.length <= 15) {\n    international = KNOWN_CODES.some(function (c) { return d.startsWith(c); });\n  }\n  if (!international) {\n    d = cc + d.replace(/^0+/, '');\n  }\n  return (d.length >= 8 && d.length <= 15) ? d : '';\n}\n\nfunction languageFor(phone) {\n  const table = s.languageByPrefix || {};\n  const hit = Object.keys(table)\n    .filter(function (p) { return phone.startsWith(p); })\n    .sort(function (a, b) { return b.length - a.length; })[0];\n  return hit ? table[hit] : (s.defaultLanguage || 'en');\n}\n\nconst rawPhone = (lead.PHONE && lead.PHONE[0] && lead.PHONE[0].VALUE) || '';\nconst phone = toE164(rawPhone, s.defaultCountryCode);\n\n// Dedupe. The record lives in this workflow's static data, keyed by phone number.\n// Caveat worth knowing: static data is per workflow and is reset when the workflow\n// is deleted, duplicated or re-imported - after that the first lead from a known\n// number is welcomed once more. It is not a database; for a hard guarantee, keep\n// the flag on the lead itself (a custom field) instead.\nconst store = $getWorkflowStaticData('global');\nstore.leads = store.leads || {};\nconst windowMs = Number(s.dedupeDays || 7) * 24 * 60 * 60 * 1000;\nconst previous = phone ? store.leads[phone] : null;\nconst alreadyWelcomed = !!(previous && previous.welcomedAt &&\n  (Date.now() - previous.welcomedAt) < windowMs);\n\nlet decision = 'send';\nlet skipReason = '';\nif (!phone) {\n  decision = 'no_phone';\n  skipReason = 'no valid phone';\n} else if (alreadyWelcomed) {\n  decision = 'already_welcomed';\n  skipReason = 'this number was already welcomed in the last ' + s.dedupeDays + ' days';\n}\n\nreturn [{\n  json: {\n    leadId: String(lead.ID || ''),\n    firstName: lead.NAME || lead.TITLE || 'there',\n    leadTitle: lead.TITLE || '',\n    sourceId: String(lead.SOURCE_ID || ''),\n    responsibleId: String(lead.ASSIGNED_BY_ID || s.fallbackResponsibleId || ''),\n    rawPhone: rawPhone,\n    phone: phone,\n    lang: phone ? languageFor(phone) : (s.defaultLanguage || 'en'),\n    decision: decision,\n    skipReason: skipReason,\n  },\n}];"
      },
      "name": "Normalise Phone and Detect Language",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        782
      ]
    },
    {
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "Should We Send a Welcome?-rule0",
                    "leftValue": "={{ $json.decision }}",
                    "rightValue": "no_phone",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "No valid phone"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "Should We Send a Welcome?-rule1",
                    "leftValue": "={{ $json.decision }}",
                    "rightValue": "already_welcomed",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Welcomed recently"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "Should We Send a Welcome?-rule2",
                    "leftValue": "={{ $json.decision }}",
                    "rightValue": "send",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Send a welcome"
            }
          ]
        },
        "options": {}
      },
      "name": "Should We Send a Welcome?",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        960,
        782
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Settings').item.json.bitrixPortalUrl }}/rest/{{ $('Settings').item.json.bitrixWebhookToken }}/crm.timeline.comment.add",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "options": {},
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ fields: { ENTITY_ID: Number($json.leadId), ENTITY_TYPE: 'lead', COMMENT: 'WhatsApp welcome skipped: ' + $json.skipReason } }) }}"
      },
      "name": "Log Skipped Lead in Bitrix24",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1280,
        782
      ]
    },
    {
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "Route by Lead Source-rule0",
                    "leftValue": "={{ $json.sourceId }}",
                    "rightValue": "={{ $('Settings').item.json.sourceWeb }}",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Website"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "Route by Lead Source-rule1",
                    "leftValue": "={{ $json.sourceId }}",
                    "rightValue": "={{ $('Settings').item.json.sourceCall }}",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Call-back"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "Route by Lead Source-rule2",
                    "leftValue": "={{ $json.sourceId }}",
                    "rightValue": "={{ $('Settings').item.json.sourceAdvertising }}",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Advertising"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "Anything else"
        }
      },
      "name": "Route by Lead Source",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        1280,
        982
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "Website Lead Text-0",
              "name": "templateKey",
              "value": "welcome_web",
              "type": "string"
            },
            {
              "id": "Website Lead Text-1",
              "name": "messageText",
              "value": "={{ ($('Settings').item.json.templates[$json.lang + '.welcome_web'] || $('Settings').item.json.templates['en.welcome_web']).replace(/\\{name\\}/g, $json.firstName).replace(/\\{company\\}/g, $('Settings').item.json.companyName) }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "name": "Website Lead Text",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1600,
        982
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "Call-Back Lead Text-0",
              "name": "templateKey",
              "value": "welcome_call",
              "type": "string"
            },
            {
              "id": "Call-Back Lead Text-1",
              "name": "messageText",
              "value": "={{ ($('Settings').item.json.templates[$json.lang + '.welcome_call'] || $('Settings').item.json.templates['en.welcome_call']).replace(/\\{name\\}/g, $json.firstName).replace(/\\{company\\}/g, $('Settings').item.json.companyName) }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "name": "Call-Back Lead Text",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1600,
        1182
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "Advertising Lead Text-0",
              "name": "templateKey",
              "value": "welcome_ad",
              "type": "string"
            },
            {
              "id": "Advertising Lead Text-1",
              "name": "messageText",
              "value": "={{ ($('Settings').item.json.templates[$json.lang + '.welcome_ad'] || $('Settings').item.json.templates['en.welcome_ad']).replace(/\\{name\\}/g, $json.firstName).replace(/\\{company\\}/g, $('Settings').item.json.companyName) }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "name": "Advertising Lead Text",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1600,
        1382
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "Other Lead Text-0",
              "name": "templateKey",
              "value": "welcome_other",
              "type": "string"
            },
            {
              "id": "Other Lead Text-1",
              "name": "messageText",
              "value": "={{ ($('Settings').item.json.templates[$json.lang + '.welcome_other'] || $('Settings').item.json.templates['en.welcome_other']).replace(/\\{name\\}/g, $json.firstName).replace(/\\{company\\}/g, $('Settings').item.json.companyName) }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "name": "Other Lead Text",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1600,
        1582
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "connectionId": "={{ $('Settings').item.json.wasyncConnectionId }}",
        "to": "={{ $('Normalise Phone and Detect Language').item.json.phone }}",
        "text": "={{ $json.messageText }}",
        "additionalFields": {}
      },
      "name": "Send Welcome Message on WhatsApp",
      "type": "n8n-nodes-wasync.wasync",
      "typeVersion": 1,
      "position": [
        1920,
        982
      ],
      "credentials": {
        "wasyncApi": {
          "name": "WASync account"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "WhatsApp Send Succeeded?-cond0",
              "leftValue": "={{ $json.error ? 'failed' : 'sent' }}",
              "rightValue": "sent",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "WhatsApp Send Succeeded?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2240,
        982
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// The welcome went out. Remember it (the dedupe check and the hourly sweep both read\n// this record), then build ONE Bitrix24 batch call that does three things at once:\n// write the timeline note, move the lead to the Contacted stage, and notify the\n// responsible person.\nconst s = $('Settings').first().json;\nconst lead = $('Normalise Phone and Detect Language').first().json;\nconst enc = encodeURIComponent;\n\n// Only one of the four source branches ran, so ask each in turn for the text it\n// built. Referencing a node that did not run throws, hence the try/catch.\nlet sentText = '';\nconst branches = ['Website Lead Text', 'Call-Back Lead Text', 'Advertising Lead Text', 'Other Lead Text'];\nfor (const branch of branches) {\n  try {\n    const item = $(branch).first();\n    if (item && item.json && item.json.messageText) { sentText = item.json.messageText; break; }\n  } catch (e) { /* that branch did not run */ }\n}\n\nconst store = $getWorkflowStaticData('global');\nstore.leads = store.leads || {};\nstore.leads[lead.phone] = {\n  leadId: lead.leadId,\n  lang: lead.lang,\n  firstName: lead.firstName,\n  responsibleId: lead.responsibleId,\n  welcomedAt: Date.now(),\n  repliedAt: null,\n  secondTouchAt: null,\n  noResponseAt: null,\n};\n\nconst leadUrl = s.bitrixPortalUrl + '/crm/lead/details/' + lead.leadId + '/';\nconst cmd = {\n  comment: 'crm.timeline.comment.add?fields[ENTITY_ID]=' + enc(lead.leadId) +\n    '&fields[ENTITY_TYPE]=lead&fields[COMMENT]=' +\n    enc('WhatsApp welcome sent to +' + lead.phone + ':\\n' + sentText),\n  stage: 'crm.lead.update?id=' + enc(lead.leadId) +\n    '&fields[STATUS_ID]=' + enc(s.stageContacted),\n  notify: 'im.notify.system.add?USER_ID=' + enc(lead.responsibleId) +\n    '&MESSAGE=' + enc('WhatsApp welcome sent to ' + lead.firstName + ' (+' + lead.phone + '). ' + leadUrl),\n};\n\nreturn [{ json: { leadId: lead.leadId, phone: lead.phone, sentText: sentText, batchBody: JSON.stringify({ halt: 0, cmd: cmd }) } }];"
      },
      "name": "Record Welcome and Build CRM Update",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2560,
        982
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Settings').item.json.bitrixPortalUrl }}/rest/{{ $('Settings').item.json.bitrixWebhookToken }}/batch",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "options": {},
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ $json.batchBody }}"
      },
      "name": "Log, Move Stage and Notify in Bitrix24",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2880,
        982
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Settings').item.json.bitrixPortalUrl }}/rest/{{ $('Settings').item.json.bitrixWebhookToken }}/batch",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "options": {},
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ halt: 0, cmd: { task: 'tasks.task.add?fields[TITLE]=' + encodeURIComponent('WhatsApp welcome could not be delivered to lead ' + $('Normalise Phone and Detect Language').item.json.leadId) + '&fields[RESPONSIBLE_ID]=' + encodeURIComponent($('Normalise Phone and Detect Language').item.json.responsibleId) + '&fields[UF_CRM_TASK][0]=' + encodeURIComponent('L_' + $('Normalise Phone and Detect Language').item.json.leadId) + '&fields[DESCRIPTION]=' + encodeURIComponent('The WhatsApp welcome message failed: ' + ($json.error || 'unknown error') + '. Number: +' + $('Normalise Phone and Detect Language').item.json.phone + '. Please reach this lead another way: ' + $('Settings').item.json.bitrixPortalUrl + '/crm/lead/details/' + $('Normalise Phone and Detect Language').item.json.leadId + '/'), comment: 'crm.timeline.comment.add?fields[ENTITY_ID]=' + encodeURIComponent($('Normalise Phone and Detect Language').item.json.leadId) + '&fields[ENTITY_TYPE]=lead&fields[COMMENT]=' + encodeURIComponent('WhatsApp welcome failed: ' + ($json.error || 'unknown error')) } }) }}"
      },
      "name": "Create Task and Log Failure in Bitrix24",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2560,
        1182
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "wasync-inbound",
        "responseMode": "onReceived",
        "options": {}
      },
      "name": "Receive WhatsApp Reply",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        2582
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "Settings (Replies)-0",
              "name": "bitrixPortalUrl",
              "value": "https://yourportal.bitrix24.com",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-1",
              "name": "bitrixWebhookToken",
              "value": "REPLACE_WITH_YOUR_INBOUND_WEBHOOK_TOKEN",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-2",
              "name": "companyName",
              "value": "Your Company",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-3",
              "name": "wasyncConnectionId",
              "value": "REPLACE_WITH_YOUR_WASYNC_CONNECTION_ID",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-4",
              "name": "defaultCountryCode",
              "value": "40",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-5",
              "name": "dedupeDays",
              "value": 7,
              "type": "number"
            },
            {
              "id": "Settings (Replies)-6",
              "name": "secondTouchAfterHours",
              "value": 24,
              "type": "number"
            },
            {
              "id": "Settings (Replies)-7",
              "name": "noResponseAfterHours",
              "value": 72,
              "type": "number"
            },
            {
              "id": "Settings (Replies)-8",
              "name": "stageContacted",
              "value": "IN_PROCESS",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-9",
              "name": "stageReplied",
              "value": "UC_REPLIED",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-10",
              "name": "stageNoResponse",
              "value": "UC_NO_RESPONSE",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-11",
              "name": "sourceWeb",
              "value": "WEB",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-12",
              "name": "sourceCall",
              "value": "CALLBACK",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-13",
              "name": "sourceAdvertising",
              "value": "ADVERTISING",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-14",
              "name": "fallbackResponsibleId",
              "value": "1",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-15",
              "name": "defaultLanguage",
              "value": "en",
              "type": "string"
            },
            {
              "id": "Settings (Replies)-16",
              "name": "languageByPrefix",
              "value": "={{ {\"40\": \"ro\", \"966\": \"ar\", \"971\": \"ar\"} }}",
              "type": "object"
            },
            {
              "id": "Settings (Replies)-17",
              "name": "templates",
              "value": "={{ {\"en.welcome_web\": \"Hi {name}, thanks for your enquiry on our website. This is {company} — you can reply right here on WhatsApp and we will get back to you within the hour.\", \"en.welcome_call\": \"Hi {name}, this is {company}. We received your call-back request. What is the best time to call you? You can also just reply here.\", \"en.welcome_ad\": \"Hi {name}, thanks for your interest in our offer. This is {company} — ask us anything here on WhatsApp and we will answer straight away.\", \"en.welcome_other\": \"Hi {name}, this is {company}. Thanks for getting in touch — how can we help?\", \"en.second_touch\": \"Hi {name}, just making sure our message reached you. {company} is still happy to help — reply here whenever it suits you.\", \"ro.welcome_web\": \"Bună {name}, mulțumim pentru mesajul trimis pe site. Suntem {company} — ne puteți răspunde direct aici, pe WhatsApp, și revenim în cel mult o oră.\", \"ro.welcome_call\": \"Bună {name}, aici {company}. Am primit solicitarea de a fi sunat. La ce oră vă putem apela? Ne puteți răspunde și aici.\", \"ro.welcome_ad\": \"Bună {name}, mulțumim pentru interesul față de oferta noastră. Suntem {company} — întrebați-ne orice aici, pe WhatsApp.\", \"ro.welcome_other\": \"Bună {name}, aici {company}. Mulțumim că ne-ați scris — cu ce vă putem ajuta?\", \"ro.second_touch\": \"Bună {name}, verificăm doar dacă v-a ajuns mesajul nostru. {company} vă stă la dispoziție — ne puteți scrie oricând.\", \"ar.welcome_web\": \"مرحباً {name}، شكراً لتواصلك عبر موقعنا. نحن {company} — يمكنك الرد هنا على واتساب وسنعود إليك خلال ساعة.\", \"ar.welcome_call\": \"مرحباً {name}، معك {company}. وصلنا طلب الاتصال بك. ما الوقت المناسب للاتصال؟ يمكنك أيضاً الرد هنا.\", \"ar.welcome_ad\": \"مرحباً {name}، شكراً لاهتمامك بعرضنا. نحن {company} — اسألنا عن أي شيء هنا على واتساب.\", \"ar.welcome_other\": \"مرحباً {name}، معك {company}. شكراً لتواصلك — كيف يمكننا مساعدتك؟\", \"ar.second_touch\": \"مرحباً {name}، نتأكد فقط من وصول رسالتنا إليك. {company} في خدمتك — يمكنك الرد في أي وقت.\"} }}",
              "type": "object"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "name": "Settings (Replies)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        0,
        2156
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $('Settings (Replies)').item.json.bitrixPortalUrl }}/rest/{{ $('Settings (Replies)').item.json.bitrixWebhookToken }}/crm.lead.list",
        "options": {},
        "sendQuery": true,
        "specifyQuery": "keypair",
        "queryParameters": {
          "parameters": [
            {
              "name": "filter[%PHONE]",
              "value": "={{ $('Receive WhatsApp Reply').item.json.body.message.from.replace(/\\D/g, '').slice(-9) }}"
            },
            {
              "name": "select[0]",
              "value": "ID"
            },
            {
              "name": "select[1]",
              "value": "TITLE"
            },
            {
              "name": "select[2]",
              "value": "NAME"
            },
            {
              "name": "select[3]",
              "value": "STATUS_ID"
            },
            {
              "name": "select[4]",
              "value": "ASSIGNED_BY_ID"
            },
            {
              "name": "select[5]",
              "value": "PHONE"
            },
            {
              "name": "order[ID]",
              "value": "DESC"
            }
          ]
        }
      },
      "name": "Find Lead by Phone in Bitrix24",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        320,
        2582
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "Lead Waiting for a Reply?-cond0",
              "leftValue": "={{ $('Receive WhatsApp Reply').item.json.body.event }}",
              "rightValue": "message.received",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "Lead Waiting for a Reply?-cond1",
              "leftValue": "={{ ($json.result || []).length && $json.result[0].STATUS_ID === $('Settings (Replies)').item.json.stageContacted ? 'yes' : 'no' }}",
              "rightValue": "yes",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Lead Waiting for a Reply?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        640,
        2582
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// The customer answered. Mark the stored record as replied (so the hourly sweep stops\n// chasing them) and build one Bitrix24 batch call: move the stage, write what they\n// said on the timeline, notify the responsible person.\nconst s = $('Settings (Replies)').first().json;\nconst hook = $('Receive WhatsApp Reply').first().json.body || {};\nconst message = hook.message || {};\nconst lead = (($input.first().json || {}).result || [])[0] || {};\nconst enc = encodeURIComponent;\n\nconst senderDigits = String(message.from || '').replace(/\\D/g, '');\nconst store = $getWorkflowStaticData('global');\nstore.leads = store.leads || {};\nif (store.leads[senderDigits]) {\n  store.leads[senderDigits].repliedAt = Date.now();\n}\n\nconst leadId = String(lead.ID || '');\nconst responsibleId = String(lead.ASSIGNED_BY_ID || s.fallbackResponsibleId || '');\nconst said = String(message.text || '(no text - media message)').slice(0, 500);\nconst leadUrl = s.bitrixPortalUrl + '/crm/lead/details/' + leadId + '/';\n\nconst cmd = {\n  stage: 'crm.lead.update?id=' + enc(leadId) + '&fields[STATUS_ID]=' + enc(s.stageReplied),\n  comment: 'crm.timeline.comment.add?fields[ENTITY_ID]=' + enc(leadId) +\n    '&fields[ENTITY_TYPE]=lead&fields[COMMENT]=' + enc('Lead replied on WhatsApp: ' + said),\n  notify: 'im.notify.system.add?USER_ID=' + enc(responsibleId) +\n    '&MESSAGE=' + enc((lead.TITLE || 'A lead') + ' replied on WhatsApp: ' + said + ' ' + leadUrl),\n};\n\nreturn [{ json: { leadId: leadId, phone: senderDigits, batchBody: JSON.stringify({ halt: 0, cmd: cmd }) } }];"
      },
      "name": "Mark Lead as Replied",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        960,
        2582
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Settings (Replies)').item.json.bitrixPortalUrl }}/rest/{{ $('Settings (Replies)').item.json.bitrixWebhookToken }}/batch",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "options": {},
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ $json.batchBody }}"
      },
      "name": "Log Reply, Move Stage and Notify in Bitrix24",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1280,
        2582
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 1
            }
          ]
        }
      },
      "name": "Run Every Hour",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.4,
      "position": [
        0,
        3582
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "Settings (Follow-Up)-0",
              "name": "bitrixPortalUrl",
              "value": "https://yourportal.bitrix24.com",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-1",
              "name": "bitrixWebhookToken",
              "value": "REPLACE_WITH_YOUR_INBOUND_WEBHOOK_TOKEN",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-2",
              "name": "companyName",
              "value": "Your Company",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-3",
              "name": "wasyncConnectionId",
              "value": "REPLACE_WITH_YOUR_WASYNC_CONNECTION_ID",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-4",
              "name": "defaultCountryCode",
              "value": "40",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-5",
              "name": "dedupeDays",
              "value": 7,
              "type": "number"
            },
            {
              "id": "Settings (Follow-Up)-6",
              "name": "secondTouchAfterHours",
              "value": 24,
              "type": "number"
            },
            {
              "id": "Settings (Follow-Up)-7",
              "name": "noResponseAfterHours",
              "value": 72,
              "type": "number"
            },
            {
              "id": "Settings (Follow-Up)-8",
              "name": "stageContacted",
              "value": "IN_PROCESS",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-9",
              "name": "stageReplied",
              "value": "UC_REPLIED",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-10",
              "name": "stageNoResponse",
              "value": "UC_NO_RESPONSE",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-11",
              "name": "sourceWeb",
              "value": "WEB",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-12",
              "name": "sourceCall",
              "value": "CALLBACK",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-13",
              "name": "sourceAdvertising",
              "value": "ADVERTISING",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-14",
              "name": "fallbackResponsibleId",
              "value": "1",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-15",
              "name": "defaultLanguage",
              "value": "en",
              "type": "string"
            },
            {
              "id": "Settings (Follow-Up)-16",
              "name": "languageByPrefix",
              "value": "={{ {\"40\": \"ro\", \"966\": \"ar\", \"971\": \"ar\"} }}",
              "type": "object"
            },
            {
              "id": "Settings (Follow-Up)-17",
              "name": "templates",
              "value": "={{ {\"en.welcome_web\": \"Hi {name}, thanks for your enquiry on our website. This is {company} — you can reply right here on WhatsApp and we will get back to you within the hour.\", \"en.welcome_call\": \"Hi {name}, this is {company}. We received your call-back request. What is the best time to call you? You can also just reply here.\", \"en.welcome_ad\": \"Hi {name}, thanks for your interest in our offer. This is {company} — ask us anything here on WhatsApp and we will answer straight away.\", \"en.welcome_other\": \"Hi {name}, this is {company}. Thanks for getting in touch — how can we help?\", \"en.second_touch\": \"Hi {name}, just making sure our message reached you. {company} is still happy to help — reply here whenever it suits you.\", \"ro.welcome_web\": \"Bună {name}, mulțumim pentru mesajul trimis pe site. Suntem {company} — ne puteți răspunde direct aici, pe WhatsApp, și revenim în cel mult o oră.\", \"ro.welcome_call\": \"Bună {name}, aici {company}. Am primit solicitarea de a fi sunat. La ce oră vă putem apela? Ne puteți răspunde și aici.\", \"ro.welcome_ad\": \"Bună {name}, mulțumim pentru interesul față de oferta noastră. Suntem {company} — întrebați-ne orice aici, pe WhatsApp.\", \"ro.welcome_other\": \"Bună {name}, aici {company}. Mulțumim că ne-ați scris — cu ce vă putem ajuta?\", \"ro.second_touch\": \"Bună {name}, verificăm doar dacă v-a ajuns mesajul nostru. {company} vă stă la dispoziție — ne puteți scrie oricând.\", \"ar.welcome_web\": \"مرحباً {name}، شكراً لتواصلك عبر موقعنا. نحن {company} — يمكنك الرد هنا على واتساب وسنعود إليك خلال ساعة.\", \"ar.welcome_call\": \"مرحباً {name}، معك {company}. وصلنا طلب الاتصال بك. ما الوقت المناسب للاتصال؟ يمكنك أيضاً الرد هنا.\", \"ar.welcome_ad\": \"مرحباً {name}، شكراً لاهتمامك بعرضنا. نحن {company} — اسألنا عن أي شيء هنا على واتساب.\", \"ar.welcome_other\": \"مرحباً {name}، معك {company}. شكراً لتواصلك — كيف يمكننا مساعدتك؟\", \"ar.second_touch\": \"مرحباً {name}، نتأكد فقط من وصول رسالتنا إليك. {company} في خدمتك — يمكنك الرد في أي وقت.\"} }}",
              "type": "object"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "name": "Settings (Follow-Up)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        0,
        3156
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// The hourly sweep. Everything it needs is in the workflow's static data, written by\n// the two lanes above - no CRM query, no cost when there is nothing to do.\n//\n//   welcomed > secondTouchAfterHours ago, no reply, no second touch yet -> one more message\n//   welcomed > noResponseAfterHours ago, no reply                       -> No-response stage\n//   welcomed > dedupeDays ago                                           -> forget the number\n//\n// Records are marked BEFORE the message leaves, on purpose: if a send fails, this\n// workflow must not retry it every hour for three days.\nconst s = $('Settings (Follow-Up)').first().json;\nconst store = $getWorkflowStaticData('global');\nstore.leads = store.leads || {};\nconst enc = encodeURIComponent;\nconst now = Date.now();\nconst pruneMs = Number(s.dedupeDays || 7) * 24 * 60 * 60 * 1000;\nconst secondMs = Number(s.secondTouchAfterHours || 24) * 60 * 60 * 1000;\nconst giveUpMs = Number(s.noResponseAfterHours || 72) * 60 * 60 * 1000;\nconst templates = s.templates || {};\n\nfunction template(key, lang, firstName) {\n  const text = templates[lang + '.' + key] || templates['en.' + key] || '';\n  return text.replace(/\\{name\\}/g, firstName || 'there')\n             .replace(/\\{company\\}/g, s.companyName || '');\n}\n\nconst due = [];\nfor (const entry of Object.entries(store.leads)) {\n  const phone = entry[0];\n  const rec = entry[1];\n  if (!rec || !rec.welcomedAt) { delete store.leads[phone]; continue; }\n  const age = now - rec.welcomedAt;\n  if (age > pruneMs) { delete store.leads[phone]; continue; }\n  if (rec.repliedAt) continue;\n\n  const leadUrl = s.bitrixPortalUrl + '/crm/lead/details/' + rec.leadId + '/';\n\n  if (!rec.secondTouchAt && age > secondMs) {\n    rec.secondTouchAt = now;\n    const text = template('second_touch', rec.lang, rec.firstName);\n    due.push({\n      json: {\n        action: 'second_touch',\n        phone: phone,\n        leadId: rec.leadId,\n        messageText: text,\n        batchBody: JSON.stringify({\n          halt: 0,\n          cmd: {\n            comment: 'crm.timeline.comment.add?fields[ENTITY_ID]=' + enc(rec.leadId) +\n              '&fields[ENTITY_TYPE]=lead&fields[COMMENT]=' +\n              enc('WhatsApp follow-up sent (no reply after ' + s.secondTouchAfterHours + ' h):\\n' + text),\n          },\n        }),\n      },\n    });\n    continue;\n  }\n\n  if (!rec.noResponseAt && age > giveUpMs) {\n    rec.noResponseAt = now;\n    due.push({\n      json: {\n        action: 'no_response',\n        phone: phone,\n        leadId: rec.leadId,\n        messageText: '',\n        batchBody: JSON.stringify({\n          halt: 0,\n          cmd: {\n            stage: 'crm.lead.update?id=' + enc(rec.leadId) +\n              '&fields[STATUS_ID]=' + enc(s.stageNoResponse),\n            comment: 'crm.timeline.comment.add?fields[ENTITY_ID]=' + enc(rec.leadId) +\n              '&fields[ENTITY_TYPE]=lead&fields[COMMENT]=' +\n              enc('No answer on WhatsApp after ' + s.noResponseAfterHours + ' h. ' + leadUrl),\n          },\n        }),\n      },\n    });\n  }\n}\n\nreturn due;"
      },
      "name": "Pick Leads Due for Follow-Up",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        320,
        3582
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "Needs a Second Message?-cond0",
              "leftValue": "={{ $json.action }}",
              "rightValue": "second_touch",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Needs a Second Message?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        640,
        3582
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "connectionId": "={{ $('Settings (Follow-Up)').item.json.wasyncConnectionId }}",
        "to": "={{ $json.phone }}",
        "text": "={{ $json.messageText }}",
        "additionalFields": {}
      },
      "name": "Send Second Message on WhatsApp",
      "type": "n8n-nodes-wasync.wasync",
      "typeVersion": 1,
      "position": [
        960,
        3582
      ],
      "credentials": {
        "wasyncApi": {
          "name": "WASync account"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Settings (Follow-Up)').item.json.bitrixPortalUrl }}/rest/{{ $('Settings (Follow-Up)').item.json.bitrixWebhookToken }}/batch",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "options": {},
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ $('Pick Leads Due for Follow-Up').item.json.batchBody }}"
      },
      "name": "Update Bitrix24 for the Follow-Up",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1280,
        3582
      ]
    }
  ],
  "connections": {
    "Receive New Lead from Bitrix24": {
      "main": [
        [
          {
            "node": "Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Settings": {
      "main": [
        [
          {
            "node": "Get Lead from Bitrix24",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Lead from Bitrix24": {
      "main": [
        [
          {
            "node": "Normalise Phone and Detect Language",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalise Phone and Detect Language": {
      "main": [
        [
          {
            "node": "Should We Send a Welcome?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Should We Send a Welcome?": {
      "main": [
        [
          {
            "node": "Log Skipped Lead in Bitrix24",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Skipped Lead in Bitrix24",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Route by Lead Source",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Lead Source": {
      "main": [
        [
          {
            "node": "Website Lead Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Call-Back Lead Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Advertising Lead Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Other Lead Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Website Lead Text": {
      "main": [
        [
          {
            "node": "Send Welcome Message on WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call-Back Lead Text": {
      "main": [
        [
          {
            "node": "Send Welcome Message on WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Advertising Lead Text": {
      "main": [
        [
          {
            "node": "Send Welcome Message on WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Other Lead Text": {
      "main": [
        [
          {
            "node": "Send Welcome Message on WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Welcome Message on WhatsApp": {
      "main": [
        [
          {
            "node": "WhatsApp Send Succeeded?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WhatsApp Send Succeeded?": {
      "main": [
        [
          {
            "node": "Record Welcome and Build CRM Update",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create Task and Log Failure in Bitrix24",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Record Welcome and Build CRM Update": {
      "main": [
        [
          {
            "node": "Log, Move Stage and Notify in Bitrix24",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive WhatsApp Reply": {
      "main": [
        [
          {
            "node": "Settings (Replies)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Settings (Replies)": {
      "main": [
        [
          {
            "node": "Find Lead by Phone in Bitrix24",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Lead by Phone in Bitrix24": {
      "main": [
        [
          {
            "node": "Lead Waiting for a Reply?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lead Waiting for a Reply?": {
      "main": [
        [
          {
            "node": "Mark Lead as Replied",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Mark Lead as Replied": {
      "main": [
        [
          {
            "node": "Log Reply, Move Stage and Notify in Bitrix24",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Every Hour": {
      "main": [
        [
          {
            "node": "Settings (Follow-Up)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Settings (Follow-Up)": {
      "main": [
        [
          {
            "node": "Pick Leads Due for Follow-Up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pick Leads Due for Follow-Up": {
      "main": [
        [
          {
            "node": "Needs a Second Message?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Needs a Second Message?": {
      "main": [
        [
          {
            "node": "Send Second Message on WhatsApp",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Bitrix24 for the Follow-Up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Second Message on WhatsApp": {
      "main": [
        [
          {
            "node": "Update Bitrix24 for the Follow-Up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
