{
  "name": "WhatsApp Templates - Secure Credentials",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "send-whatsapp-template",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "05756597-b904-4953-b13f-ee9fa290110c",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        13280,
        9888
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// PrepareData - \u0627\u0633\u062a\u062e\u0631\u0627\u062c \u0648\u0627\u0644\u062a\u062d\u0642\u0642 \u0645\u0646 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a\n// \ud83d\udd10 \u0627\u0644\u062a\u0648\u0643\u0646 \u064a\u064f\u0642\u0631\u0623 \u0645\u0646 n8n Credentials (\u0623\u0645\u0627\u0646 \u0623\u0639\u0644\u0649)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nconst inputData = $input.first().json;\nconst data = inputData.body || inputData;\n\n// \u0627\u0644\u062a\u062d\u0642\u0642 \u0645\u0646 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0645\u0637\u0644\u0648\u0628\u0629 (\u0628\u062f\u0648\u0646 accessToken - \u064a\u064f\u0642\u0631\u0623 \u0645\u0646 Credentials)\nconst templateType = data.templateType;\nconst recipients = data.recipients || [];\nconst phoneNumberId = data.phoneNumberId || '';\n\nif (!templateType) {\n  return [{ json: { success: false, error: 'templateType \u0645\u0637\u0644\u0648\u0628', hasError: true } }];\n}\nif (!phoneNumberId) {\n  return [{ json: { success: false, error: 'phoneNumberId \u0645\u0637\u0644\u0648\u0628', hasError: true } }];\n}\nif (!recipients || recipients.length === 0) {\n  return [{ json: { success: false, error: 'recipients \u0645\u0637\u0644\u0648\u0628\u0629', hasError: true } }];\n}\n\nconst defaultValues = {\n  contactNumbers: data.contactNumbers || '07705210210 - 07717727720',\n  location: data.location || '\u0628\u063a\u062f\u0627\u062f/\u062d\u064a \u0627\u0644\u0627\u0645\u0627\u0646\u0629 https://maps.app.goo.gl/hfYqRMZNr2qYnsV3A',\n  offerText: data.offerText || '\u0644\u062f\u064a\u0646\u0627 \u0639\u0631\u0648\u0636 \u0645\u0645\u064a\u0632\u0629 \u0644\u0643!'\n};\n\nconst config = {\n  maxRetries: data.maxRetries || 3,\n  progressUpdateEvery: data.progressUpdateEvery || 100\n};\n\nconst batchId = `batch_${Date.now()}`;\nconst total = recipients.length;\n\nreturn recipients.map((r, index) => {\n  let phone = (r.phoneNumber || r.phone || '').toString().trim();\n  if (phone.startsWith('0')) {\n    phone = '964' + phone.substring(1);\n  } else if (!phone.startsWith('964') && !phone.startsWith('+964')) {\n    phone = '964' + phone;\n  }\n  phone = phone.replace('+', '').replace(/[^\\d]/g, '');\n  \n  const valid = /^964\\d{9,12}$/.test(phone);\n  \n  return {\n    json: {\n      index: index + 1,\n      total: total,\n      batchId: batchId,\n      name: r.name || '\u0639\u0632\u064a\u0632\u064a \u0627\u0644\u0645\u0634\u062a\u0631\u0643',\n      phone: phone,\n      originalPhone: r.phoneNumber || r.phone || '',\n      expiryDate: r.expiryDate || r.expires || '',\n      planName: r.planName || r.plan || '',\n      price: r.price || '',\n      templateType: templateType,\n      phoneNumberId: phoneNumberId,\n      contactNumbers: r.contactNumbers || defaultValues.contactNumbers,\n      location: r.location || defaultValues.location,\n      offerText: r.offerText || defaultValues.offerText,\n      valid: valid,\n      err: valid ? '' : 'Invalid phone format',\n      hasError: false,\n      retryCount: 0,\n      config: config\n    }\n  };\n});"
      },
      "id": "bf433526-9b51-4bee-b133-2fe8e58e03bb",
      "name": "PrepareData",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        13504,
        9888
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u062a\u062c\u0647\u064a\u0632 \u0627\u0644\u0631\u062f \u0627\u0644\u0641\u0648\u0631\u064a\nconst items = $items('PrepareData').map(i => i.json);\nconst first = items[0] || {};\n\nif (first.hasError) {\n  return [{\n    json: {\n      immediateResponse: {\n        success: false,\n        error: first.error\n      },\n      hasError: true\n    }\n  }];\n}\n\nconst totalCount = items.length;\nconst batchId = first.batchId || `batch_${Date.now()}`;\nconst templateType = first.templateType || '';\nconst config = first.config || {};\n\nreturn [{\n  json: {\n    immediateResponse: {\n      success: true,\n      status: 'processing',\n      message: '\u062a\u0645 \u0627\u0633\u062a\u0644\u0627\u0645 \u0627\u0644\u0637\u0644\u0628 \u0648\u062c\u0627\u0631\u064a \u0625\u0631\u0633\u0627\u0644 \u0627\u0644\u0631\u0633\u0627\u0626\u0644 \u0641\u064a \u0627\u0644\u062e\u0644\u0641\u064a\u0629',\n      batchId,\n      templateType,\n      totalRecipients: totalCount,\n      estimatedTime: Math.ceil(totalCount * 1.5) + ' \u062b\u0627\u0646\u064a\u0629',\n      features: {\n        retryEnabled: true,\n        maxRetries: config.maxRetries || 3,\n        secureCredentials: true\n      },\n      timestamp: new Date().toISOString()\n    },\n    processItems: items,\n    batchId,\n    totalCount,\n    config,\n    hasError: false\n  }\n}];"
      },
      "id": "0fac9f87-c8ff-40cb-b2e2-0fa1af6cf313",
      "name": "PrepareImmediateResponse",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        13728,
        9888
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json.immediateResponse }}",
        "options": {
          "responseCode": 200
        }
      },
      "id": "6e9c5500-a45e-448a-a476-cb9711c305fc",
      "name": "RespondImmediately",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        13920,
        9744
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "no-error",
              "leftValue": "={{ $json.hasError }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "notEquals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "b9ade8de-c8eb-4442-982c-00c799965e13",
      "name": "CheckHasError",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        13920,
        9888
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u0627\u0633\u062a\u062e\u0631\u0627\u062c \u0627\u0644\u0639\u0646\u0627\u0635\u0631 \u0644\u0644\u0645\u0639\u0627\u0644\u062c\u0629\nconst data = $json;\nconst items = data.processItems || [];\nconst config = data.config || {};\nreturn items.map(item => ({ json: { ...item, config } }));"
      },
      "id": "f6e656f6-6e1d-44cd-9d45-dfea533688aa",
      "name": "ExtractItems",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        14144,
        9872
      ]
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "17775c9f-1076-4eea-b3ef-93fab0a13da0",
      "name": "SplitBatches",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        14368,
        9872
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "valid",
              "leftValue": "={{ $json.valid }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "862f98ce-7aa0-40d6-9401-539375023be7",
      "name": "CheckPhone",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        14528,
        9888
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u062a\u062d\u0636\u064a\u0631 \u0628\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0642\u0627\u0644\u0628 \u062d\u0633\u0628 \u0627\u0644\u0646\u0648\u0639\nconst item = $input.first().json;\nconst templateType = item.templateType;\n\nlet templateName = '';\nlet components = [];\nlet messageText = '';\n\nconst name = item.name || '\u0639\u0632\u064a\u0632\u064a \u0627\u0644\u0645\u0634\u062a\u0631\u0643';\nconst expiryDate = item.expiryDate || '';\nconst planName = item.planName || '';\nconst price = item.price || '';\nconst contactNumbers = item.contactNumbers;\nconst location = item.location;\nconst offerText = item.offerText;\n\nswitch (templateType) {\n  case 'sadara_reminder':\n    templateName = 'sadara_reminder';\n    components = [\n      {\n        \"type\": \"body\",\n        \"parameters\": [\n          { \"type\": \"text\", \"text\": name },\n          { \"type\": \"text\", \"text\": expiryDate },\n          { \"type\": \"text\", \"text\": contactNumbers },\n          { \"type\": \"text\", \"text\": location }\n        ]\n      }\n    ];\n    messageText = `\u0645\u0631\u062d\u0628\u0627\u064b ${name} \ud83d\udc4b\\n\\n\u0646\u0648\u062f \u062a\u0630\u0643\u064a\u0631\u0643 \u0628\u0623\u0646 \u0627\u0634\u062a\u0631\u0627\u0643\u0643 \u0641\u064a \u062e\u062f\u0645\u0629 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0633\u064a\u0646\u062a\u0647\u064a \u0628\u062a\u0627\u0631\u064a\u062e ${expiryDate}.\\n\\n\u0644\u0644\u062a\u062c\u062f\u064a\u062f \u0623\u0648 \u0627\u0644\u0627\u0633\u062a\u0641\u0633\u0627\u0631:\\n${contactNumbers}\\n\\n\ud83d\udccd ${location}`;\n    break;\n    \n  case 'sadara_renewed':\n    templateName = 'sadara_renewed';\n    components = [\n      {\n        \"type\": \"body\",\n        \"parameters\": [\n          { \"type\": \"text\", \"text\": name },\n          { \"type\": \"text\", \"text\": planName },\n          { \"type\": \"text\", \"text\": price.toString() },\n          { \"type\": \"text\", \"text\": expiryDate },\n          { \"type\": \"text\", \"text\": contactNumbers },\n          { \"type\": \"text\", \"text\": location }\n        ]\n      }\n    ];\n    messageText = `\u0645\u0631\u062d\u0628\u0627\u064b ${name} \ud83c\udf89\\n\\n\u062a\u0645 \u062a\u062c\u062f\u064a\u062f \u0627\u0634\u062a\u0631\u0627\u0643\u0643 \u0628\u0646\u062c\u0627\u062d!\\n\\n\u0627\u0644\u0628\u0627\u0642\u0629: ${planName}\\n\u0627\u0644\u0645\u0628\u0644\u063a: ${price} \u062f.\u0639\\n\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0627\u0646\u062a\u0647\u0627\u0621 \u0627\u0644\u062c\u062f\u064a\u062f: ${expiryDate}\\n\\n\u0644\u0644\u0627\u0633\u062a\u0641\u0633\u0627\u0631:\\n${contactNumbers}\\n\\n\ud83d\udccd ${location}`;\n    break;\n    \n  case 'sadara_expired':\n    templateName = 'sadara_expired';\n    components = [\n      {\n        \"type\": \"body\",\n        \"parameters\": [\n          { \"type\": \"text\", \"text\": name },\n          { \"type\": \"text\", \"text\": expiryDate },\n          { \"type\": \"text\", \"text\": offerText },\n          { \"type\": \"text\", \"text\": contactNumbers },\n          { \"type\": \"text\", \"text\": location }\n        ]\n      }\n    ];\n    messageText = `\u0645\u0631\u062d\u0628\u0627\u064b ${name} \ud83d\udc4b\\n\\\u0646\u0644\u0627\u062d\u0638 \u0623\u0646 \u0627\u0634\u062a\u0631\u0627\u0643\u0643 \u0642\u062f \u0627\u0646\u062a\u0647\u0649 \u0628\u062a\u0627\u0631\u064a\u062e ${expiryDate}.\\n\\n\ud83c\udf81 \u0639\u0631\u0636 \u062e\u0627\u0635: ${offerText}\\n\\n\u0644\u0644\u062a\u062c\u062f\u064a\u062f:\\n${contactNumbers}\\n\\n\ud83d\udccd ${location}`;\n    break;\n    \n  default:\n    templateName = 'sadara_reminder';\n    components = [\n      {\n        \"type\": \"body\",\n        \"parameters\": [\n          { \"type\": \"text\", \"text\": name },\n          { \"type\": \"text\", \"text\": expiryDate },\n          { \"type\": \"text\", \"text\": contactNumbers },\n          { \"type\": \"text\", \"text\": location }\n        ]\n      }\n    ];\n    messageText = `\u0645\u0631\u062d\u0628\u0627\u064b ${name} \ud83d\udc4b\\n\\\u0646\u0648\u062f \u062a\u0630\u0643\u064a\u0631\u0643 \u0628\u0623\u0646 \u0627\u0634\u062a\u0631\u0627\u0643\u0643 \u0633\u064a\u0646\u062a\u0647\u064a \u0628\u062a\u0627\u0631\u064a\u062e ${expiryDate}.\\n\\n\u0644\u0644\u062a\u062c\u062f\u064a\u062f:\\n${contactNumbers}\\n\\n\ud83d\udccd ${location}`;\n}\n\nreturn {\n  index: item.index,\n  total: item.total,\n  batchId: item.batchId,\n  phone: item.phone,\n  name: name,\n  templateType: templateType,\n  templateName: templateName,\n  components: components,\n  messageText: messageText,\n  phoneNumberId: item.phoneNumberId,\n  expiryDate: expiryDate,\n  planName: planName,\n  price: price,\n  retryCount: item.retryCount || 0,\n  config: item.config\n};"
      },
      "id": "480cef80-e2b9-46d0-ba81-f7dbaa22d4d4",
      "name": "BuildTemplate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        14736,
        9792
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/v21.0/{{ $json.phoneNumberId }}/messages",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "whatsAppApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"messaging_product\": \"whatsapp\",\n  \"to\": \"{{ $json.phone }}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"{{ $json.templateName }}\",\n    \"language\": {\n      \"code\": \"ar\"\n    },\n    \"components\": {{ JSON.stringify($json.components) }}\n  }\n}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true
            }
          }
        }
      },
      "id": "3113a3e5-832b-4b43-85c5-608cb32951e8",
      "name": "SendWhatsApp",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        14896,
        9792
      ],
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// ProcessResult - \u0645\u0639\u0627\u0644\u062c\u0629 \u0646\u062a\u064a\u062c\u0629 \u0627\u0644\u0625\u0631\u0633\u0627\u0644 \u0645\u0639 \u062f\u0639\u0645 Retry \u0648 Auth Error\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nconst response = $input.first().json;\nconst templateData = $('BuildTemplate').first().json;\nconst config = templateData.config || {};\nconst maxRetries = config.maxRetries || 3;\n\nlet success = false;\nlet messageId = '';\nlet errorMessage = '';\nlet errorCode = '';\nlet shouldRetry = false;\nlet isAuthError = false;\n\nif (response.body && response.body.messages && response.body.messages[0]) {\n  success = true;\n  messageId = response.body.messages[0].id;\n} else if (response.body && response.body.error) {\n  errorCode = response.body.error.code || '';\n  errorMessage = response.body.error.message || 'Unknown error';\n  \n  // \u062a\u0635\u0646\u064a\u0641 \u0627\u0644\u0623\u062e\u0637\u0627\u0621\n  const retryableCodes = [1, 2, 4, 17, 341, 368, 130429];\n  const authErrorCodes = [190, 102, 104, 200];\n  \n  if (authErrorCodes.includes(Number(errorCode))) {\n    isAuthError = true;\n    errorMessage = '\ud83d\udd34 \u062e\u0637\u0623 \u0645\u0635\u0627\u062f\u0642\u0629 - \u062a\u062d\u0642\u0642 \u0645\u0646 WhatsApp Access Token \u0641\u064a Credentials';\n  } else if (retryableCodes.includes(Number(errorCode))) {\n    shouldRetry = templateData.retryCount < maxRetries;\n    errorMessage = `\u062e\u0637\u0623 \u0645\u0624\u0642\u062a (${errorCode}) - ${shouldRetry ? '\u0633\u064a\u062a\u0645 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0645\u062d\u0627\u0648\u0644\u0629' : '\u0627\u0633\u062a\u0646\u0641\u062f\u062a \u0627\u0644\u0645\u062d\u0627\u0648\u0644\u0627\u062a'}`;\n  } else {\n    if (errorCode == 131030) errorMessage = '\u0631\u0642\u0645 \u0627\u0644\u0647\u0627\u062a\u0641 \u063a\u064a\u0631 \u0635\u0627\u0644\u062d \u0623\u0648 \u063a\u064a\u0631 \u0645\u0633\u062c\u0644 \u0641\u064a WhatsApp';\n    else if (errorCode == 131026) errorMessage = '\u0627\u0644\u0631\u0633\u0627\u0644\u0629 \u0644\u0645 \u062a\u064f\u0631\u0633\u0644 - \u062a\u0623\u0643\u062f \u0645\u0646 \u0646\u0627\u0641\u0630\u0629 \u0627\u0644\u0640 24 \u0633\u0627\u0639\u0629';\n    else if (errorCode == 132000) errorMessage = '\u0639\u062f\u062f \u0627\u0644\u0645\u062a\u063a\u064a\u0631\u0627\u062a \u0644\u0627 \u064a\u062a\u0637\u0627\u0628\u0642 \u0645\u0639 \u0627\u0644\u0642\u0627\u0644\u0628';\n    else if (errorCode == 132001) errorMessage = '\u0627\u0644\u0642\u0627\u0644\u0628 \u063a\u064a\u0631 \u0645\u0648\u062c\u0648\u062f \u0623\u0648 \u063a\u064a\u0631 \u0645\u0639\u062a\u0645\u062f';\n  }\n} else if (response.statusCode && response.statusCode !== 200) {\n  errorMessage = `HTTP Error: ${response.statusCode}`;\n  shouldRetry = templateData.retryCount < maxRetries && response.statusCode >= 500;\n} else {\n  errorMessage = 'No response from WhatsApp API';\n  shouldRetry = templateData.retryCount < maxRetries;\n}\n\nconst ts = Math.floor(Date.now() / 1000);\nconst tsMs = Date.now();\nconst msgId = messageId ? messageId.replace(/\\//g, '_') : 'msg_' + ts + '_' + templateData.index;\n\nconsole.log(`[${templateData.index}/${templateData.total}] Phone: ${templateData.phone}, Success: ${success}, Retry: ${templateData.retryCount}/${maxRetries}`);\n\nreturn [{\n  json: {\n    index: templateData.index,\n    total: templateData.total,\n    batchId: templateData.batchId,\n    ok: success,\n    messageId: messageId,\n    msgId: msgId,\n    err: errorMessage,\n    errorCode: errorCode,\n    phone: templateData.phone,\n    name: templateData.name,\n    templateType: templateData.templateType,\n    templateName: templateData.templateName,\n    messageText: templateData.messageText,\n    ts: ts,\n    tsMs: tsMs,\n    retryCount: templateData.retryCount,\n    shouldRetry: shouldRetry && !success,\n    isAuthError: isAuthError,\n    config: config,\n    phoneNumberId: templateData.phoneNumberId,\n    components: templateData.components,\n    expiryDate: templateData.expiryDate,\n    planName: templateData.planName,\n    price: templateData.price\n  }\n}];"
      },
      "id": "44b76f91-03a0-48dd-ad82-6ff5fb1f2ddb",
      "name": "ProcessResult",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        15056,
        9792
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "authError",
              "leftValue": "={{ $json.isAuthError }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "7086bf35-12d4-4aa4-8e9a-b777ee04539b",
      "name": "CheckAuthError",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        15216,
        9792
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// AuthErrorStop - \u0625\u064a\u0642\u0627\u0641 \u0645\u0628\u0643\u0631 \u0639\u0646\u062f \u062e\u0637\u0623 \u0645\u0635\u0627\u062f\u0642\u0629\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nconst data = $input.first().json;\n\nconsole.log('\ud83d\udd34 AUTH ERROR DETECTED - STOPPING BATCH');\nconsole.log('Batch ID:', data.batchId);\nconsole.log('Stopped at message:', data.index, 'of', data.total);\nconsole.log('\ud83d\udca1 \u062a\u0623\u0643\u062f \u0645\u0646 \u0635\u062d\u0629 WhatsApp Access Token \u0641\u064a Credentials');\n\nreturn [{ json: {\n  success: false,\n  warning: '\ud83d\udd34 \u062a\u0645 \u0625\u064a\u0642\u0627\u0641 \u0627\u0644\u0625\u0631\u0633\u0627\u0644 \u0628\u0633\u0628\u0628 \u062e\u0637\u0623 \u0645\u0635\u0627\u062f\u0642\u0629! \u062a\u062d\u0642\u0642 \u0645\u0646 WhatsApp Access Token \u0641\u064a Credentials',\n  batchId: data.batchId,\n  total: data.total,\n  sent: data.index - 1,\n  failed: data.total - data.index + 1,\n  stoppedAt: data.index,\n  rate: data.total ? ((data.index - 1) / data.total * 100).toFixed(1) + '%' : '0%',\n  completedAt: new Date().toISOString(),\n  failedSummary: 'Auth Error: ' + data.err,\n  earlyStop: true,\n  earlyStopReason: 'AUTH_ERROR'\n} }];"
      },
      "id": "1a5b65a2-489f-4dcf-a83b-545396590455",
      "name": "AuthErrorStop",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        15392,
        9680
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "shouldRetry",
              "leftValue": "={{ $json.shouldRetry }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "29655aa8-73d0-466b-b0fa-989199693222",
      "name": "CheckShouldRetry",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        15392,
        9872
      ]
    },
    {
      "parameters": {
        "amount": 2
      },
      "id": "a243b303-ffec-4eb7-98f8-8879efc54c51",
      "name": "RetryWait",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        15600,
        9776
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u062a\u062c\u0647\u064a\u0632 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0644\u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0645\u062d\u0627\u0648\u0644\u0629\nconst data = $input.first().json;\nconsole.log(`\ud83d\udd04 Retry attempt ${data.retryCount + 1} for phone: ${data.phone}`);\n\nreturn [{\n  json: {\n    index: data.index,\n    total: data.total,\n    batchId: data.batchId,\n    phone: data.phone,\n    name: data.name,\n    templateType: data.templateType,\n    templateName: data.templateName,\n    components: data.components,\n    messageText: data.messageText,\n    phoneNumberId: data.phoneNumberId,\n    expiryDate: data.expiryDate,\n    planName: data.planName,\n    price: data.price,\n    retryCount: data.retryCount + 1,\n    config: data.config\n  }\n}];"
      },
      "id": "8f124aff-4264-4a63-b005-d12e60e59013",
      "name": "PrepareRetry",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        15760,
        9776
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "isSuccess",
              "leftValue": "={{ $json.ok }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "c223a2b1-4d75-45b4-8791-84671328b6e8",
      "name": "CheckSuccess",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        15600,
        9984
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://firestore.googleapis.com/v1/projects/ramz-alsadara2025/databases/(default)/documents/whatsapp_conversations/{{ $json.phone }}/messages?documentId=sent_{{ $json.msgId }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"fields\": {\n    \"messageId\": { \"stringValue\": \"{{ $json.messageId }}\" },\n    \"phoneNumber\": { \"stringValue\": \"{{ $json.phone }}\" },\n    \"contactName\": { \"stringValue\": \"{{ $json.name }}\" },\n    \"text\": { \"stringValue\": \"{{ $json.messageText.replace(/\\\"/g, '\\\\\"').replace(/\\n/g, '\\\\n') }}\" },\n    \"templateType\": { \"stringValue\": \"{{ $json.templateType }}\" },\n    \"timestamp\": { \"integerValue\": \"{{ $json.ts }}\" },\n    \"direction\": { \"stringValue\": \"outgoing\" },\n    \"status\": { \"stringValue\": \"sent\" },\n    \"messageType\": { \"stringValue\": \"template\" },\n    \"batchId\": { \"stringValue\": \"{{ $json.batchId }}\" }\n  }\n}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "f457efce-dcc7-49d0-937c-7ab95387c711",
      "name": "SaveMessage",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        15792,
        9904
      ],
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "=https://firestore.googleapis.com/v1/projects/ramz-alsadara2025/databases/(default)/documents/whatsapp_conversations/{{ $('ProcessResult').item.json.phone }}?updateMask.fieldPaths=phoneNumber&updateMask.fieldPaths=contactName&updateMask.fieldPaths=lastMessage&updateMask.fieldPaths=lastMessageTime&updateMask.fieldPaths=updatedAt",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"fields\": {\n    \"phoneNumber\": { \"stringValue\": \"{{ $('ProcessResult').item.json.phone }}\" },\n    \"contactName\": { \"stringValue\": \"{{ $('ProcessResult').item.json.name }}\" },\n    \"lastMessage\": { \"stringValue\": \"{{ $('ProcessResult').item.json.messageText.substring(0, 100).replace(/\\\"/g, '\\\\\"').replace(/\\n/g, ' ') }}\" },\n    \"lastMessageTime\": { \"integerValue\": \"{{ $('ProcessResult').item.json.ts }}\" },\n    \"updatedAt\": { \"integerValue\": \"{{ $('ProcessResult').item.json.tsMs }}\" }\n  }\n}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "00bf6879-b57c-41fe-bf04-5f8c75ad3553",
      "name": "UpdateConversation",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        15952,
        9904
      ],
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": 1
      },
      "id": "81b4ae5c-9583-4926-9180-9ada5de02032",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        16112,
        9904
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u062a\u0645\u0631\u064a\u0631 \u0646\u062a\u064a\u062c\u0629 \u0627\u0644\u0646\u062c\u0627\u062d\nconst d = $('ProcessResult').item.json;\nreturn [{ json: { index: d.index, total: d.total, batchId: d.batchId, name: d.name, phone: d.phone, ok: true, err: '', msgId: d.msgId } }];"
      },
      "id": "f41b67ad-6de7-4464-b7af-92bbcd7a93d0",
      "name": "SendResultSuccess",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        16272,
        9904
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u062a\u0645\u0631\u064a\u0631 \u0646\u062a\u064a\u062c\u0629 \u0627\u0644\u0641\u0634\u0644\nconst d = $input.first().json;\nreturn [{ json: { index: d.index, total: d.total, batchId: d.batchId, name: d.name, phone: d.phone, ok: false, err: d.err, msgId: '' } }];"
      },
      "id": "163b424c-bd49-4b5a-b5b5-973aa066a6cc",
      "name": "SendResultFailed",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        15792,
        10080
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u0646\u062a\u064a\u062c\u0629 \u0631\u0642\u0645 \u063a\u064a\u0631 \u0635\u0627\u0644\u062d\nconst item = $input.first().json;\nreturn [{ json: { index: item.index, total: item.total, batchId: item.batchId, name: item.name, phone: item.phone, ok: false, err: item.err || 'Invalid phone format', msgId: '' } }];"
      },
      "id": "86f6bdc8-3a34-4323-925d-a79bb238bb9f",
      "name": "InvalidPhoneResult",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        14736,
        10000
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u062a\u062c\u0645\u064a\u0639 \u0627\u0644\u0646\u062a\u0627\u0626\u062c \u0627\u0644\u0646\u0647\u0627\u0626\u064a\u0629\nconst all = $input.all();\nconst sent = [], failed = [];\nlet batchId = '';\n\nfor (const it of all) {\n  const j = it.json;\n  batchId = j.batchId || batchId;\n  if (j.ok) sent.push({ index: j.index, phone: j.phone, name: j.name, msgId: j.msgId });\n  else failed.push({ index: j.index, phone: j.phone, name: j.name, err: j.err });\n}\n\nconst failRate = all.length ? (failed.length / all.length * 100) : 0;\nlet warning = '';\nif (failRate === 100) warning = '\ud83d\udd34 \u0641\u0634\u0644 \u0625\u0631\u0633\u0627\u0644 \u062c\u0645\u064a\u0639 \u0627\u0644\u0631\u0633\u0627\u0626\u0644! \u062a\u062d\u0642\u0642 \u0645\u0646 WhatsApp Credential';\nelse if (failRate > 50) warning = '\ud83d\udfe1 \u0646\u0633\u0628\u0629 \u0641\u0634\u0644 \u0639\u0627\u0644\u064a\u0629! \u062a\u062d\u0642\u0642 \u0645\u0646 \u062a\u0641\u0627\u0635\u064a\u0644 \u0627\u0644\u0623\u062e\u0637\u0627\u0621';\nelse if (failRate > 20) warning = '\ud83d\udfe0 \u0628\u0639\u0636 \u0627\u0644\u0631\u0633\u0627\u0626\u0644 \u0641\u0634\u0644\u062a - \u0631\u0627\u062c\u0639 \u0627\u0644\u062a\u0641\u0627\u0635\u064a\u0644';\n\nconst failedSummary = failed.slice(0, 10).map(f => f.phone + ':' + (f.err || 'err').substring(0, 30)).join(' | ');\n\nreturn [{ json: {\n  success: sent.length > 0,\n  warning: warning,\n  batchId: batchId,\n  total: all.length,\n  sent: sent.length,\n  failed: failed.length,\n  rate: all.length ? (sent.length/all.length*100).toFixed(1)+'%' : '0%',\n  completedAt: new Date().toISOString(),\n  failedSummary: failedSummary,\n  details: { sent, failed }\n} }];"
      },
      "id": "12286b3c-e117-4faf-a00a-3b285b073bce",
      "name": "Summary",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        14560,
        9600
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://firestore.googleapis.com/v1/projects/ramz-alsadara2025/databases/(default)/documents/whatsapp_batch_reports?documentId={{ $json.batchId }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"fields\": {\n    \"batchId\": { \"stringValue\": \"{{ $json.batchId }}\" },\n    \"total\": { \"integerValue\": \"{{ $json.total }}\" },\n    \"sent\": { \"integerValue\": \"{{ $json.sent }}\" },\n    \"failed\": { \"integerValue\": \"{{ $json.failed }}\" },\n    \"rate\": { \"stringValue\": \"{{ $json.rate }}\" },\n    \"completedAt\": { \"stringValue\": \"{{ $json.completedAt }}\" },\n    \"status\": { \"stringValue\": \"{{ $json.earlyStop ? 'stopped' : 'completed' }}\" },\n    \"warning\": { \"stringValue\": \"{{ $json.warning || '' }}\" },\n    \"failedDetails\": { \"stringValue\": \"{{ $json.failedSummary || '' }}\" },\n    \"earlyStop\": { \"booleanValue\": {{ $json.earlyStop || false }} },\n    \"earlyStopReason\": { \"stringValue\": \"{{ $json.earlyStopReason || '' }}\" }\n  }\n}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "dbf43e7a-82f0-499a-bcad-67e14f40df8f",
      "name": "SaveBatchReport",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        14720,
        9600
      ],
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// \u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u0646\u062a\u064a\u062c\u0629 \u0627\u0644\u0646\u0647\u0627\u0626\u064a\u0629\nconst result = $('Summary').first().json;\n\nconsole.log('\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550');\nconsole.log('   WhatsApp Templates - Secure Credentials');\nconsole.log('\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550');\nconsole.log('Batch ID:', result.batchId);\nconsole.log('Total:', result.total);\nconsole.log('Sent:', result.sent);\nconsole.log('Failed:', result.failed);\nconsole.log('Success Rate:', result.rate);\nif (result.warning) console.log('\u26a0\ufe0f Warning:', result.warning);\nconsole.log('\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550');\n\nreturn [{ json: result }];"
      },
      "id": "ffbc33b9-6ef5-468a-9d29-0928f8860c60",
      "name": "LogFinalResult",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        14880,
        9600
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "PrepareData",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PrepareData": {
      "main": [
        [
          {
            "node": "PrepareImmediateResponse",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PrepareImmediateResponse": {
      "main": [
        [
          {
            "node": "RespondImmediately",
            "type": "main",
            "index": 0
          },
          {
            "node": "CheckHasError",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CheckHasError": {
      "main": [
        [
          {
            "node": "ExtractItems",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ExtractItems": {
      "main": [
        [
          {
            "node": "SplitBatches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SplitBatches": {
      "main": [
        [
          {
            "node": "Summary",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "CheckPhone",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CheckPhone": {
      "main": [
        [
          {
            "node": "BuildTemplate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "InvalidPhoneResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BuildTemplate": {
      "main": [
        [
          {
            "node": "SendWhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SendWhatsApp": {
      "main": [
        [
          {
            "node": "ProcessResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ProcessResult": {
      "main": [
        [
          {
            "node": "CheckAuthError",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CheckAuthError": {
      "main": [
        [
          {
            "node": "AuthErrorStop",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "CheckShouldRetry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AuthErrorStop": {
      "main": [
        [
          {
            "node": "SaveBatchReport",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CheckShouldRetry": {
      "main": [
        [
          {
            "node": "RetryWait",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "CheckSuccess",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RetryWait": {
      "main": [
        [
          {
            "node": "PrepareRetry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PrepareRetry": {
      "main": [
        [
          {
            "node": "SendWhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CheckSuccess": {
      "main": [
        [
          {
            "node": "SaveMessage",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "SendResultFailed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SaveMessage": {
      "main": [
        [
          {
            "node": "UpdateConversation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "UpdateConversation": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "SendResultSuccess",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SendResultSuccess": {
      "main": [
        [
          {
            "node": "SplitBatches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SendResultFailed": {
      "main": [
        [
          {
            "node": "SplitBatches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "InvalidPhoneResult": {
      "main": [
        [
          {
            "node": "SplitBatches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summary": {
      "main": [
        [
          {
            "node": "SaveBatchReport",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SaveBatchReport": {
      "main": [
        [
          {
            "node": "LogFinalResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}