How to pass a mutable object in an loop?

We need to pass a mutable object into an array so that it can be processed during each iteration of the array. After all iterations, the updated object should be returned. Currently, we are making multiple API calls to get and update the object each time, but we want to optimise this workflow. Please refer to the attached sample workflow for context.

{“name”:“Rule-Engine Dev”,“nodes”:[{“parameters”:{“httpMethod”:“POST”,“path”:“e63e5a9d-4c3f-4677-a496-02c62e955e0c”,“options”:{}},“type”:“n8n-nodes-base.webhook”,“typeVersion”:2.1,“position”:[-624,-64],“id”:“fa713f47-8546-469d-8461-cda04ec7bbee”,“name”:“Webhook”,“webhookId”:“e63e5a9d-4c3f-4677-a496-02c62e955e0c”},{“parameters”:{“jsCode”:“var ruleJson = {rule:JSON.parse($(‘Loop Over Items’).first().json.ruleDefinition) }\nreturn ruleJson;”},“type”:“n8n-nodes-base.code”,“typeVersion”:2,“position”:[464,-384],“id”:“3122f88c-522b-4248-ac0d-214f6c2f1f57”,“name”:“Rule JSON”},{“parameters”:{“jsCode”:“function processRuleWithUdp(rule, udpInput, apiData1 = ) {\n \n // ========== INTERNAL HELPER FUNCTIONS ==========\n \n const arrayOperators = new Set([‘in’, ‘notIn’, ‘containsAny’, ‘containsAll’, ‘between’]);\n \n const isArrayOperator = (op) => arrayOperators.has(op);\n \n const parseArrayValue = (value) => {\n if (!value || String(value).trim() === ‘’) return ;\n try {\n const parsed = JSON.parse(value);\n if (Array.isArray(parsed)) return parsed;\n } catch { }\n return String(value).split(‘,’).map(v => v.trim()).filter(v => v !== ‘’);\n };\n \n const getFieldType = (fieldId) => {\n if (/(_Rate|_Tax|_Amount|_Total|_Value|Exchange_Rate|Disc_Amount|Discount_Percent|Penalty_Rate)$/i.test(fieldId)) return ‘number’;\n if (/(_Date|_Start|_End|Validity)$/i.test(fieldId)) return ‘date’;\n return ‘string’;\n };\n \n const convertUdpToFlatData = (udpInput) => {\n const udp = typeof udpInput === ‘string’ ? JSON.parse(udpInput) : udpInput;\n if (!udp?.documents?.length) throw new Error(‘Invalid UDP: documents missing’);\n const doc = udp.documents[0];\n if (!doc?.pages?.length) throw new Error(‘Invalid UDP: pages missing’);\n const page = doc.pages[0];\n const flat = {};\n const header = Array.isArray(page?.header) ? page.header : ;\n header.forEach((h) => {\n if (h?.name) flat[h.name] = h?.value ?? ‘’;\n });\n return flat;\n };\n \n const buildUdpFromData = (data, originalUdp, actionFields, ruleName) => {\n if (!originalUdp?.documents?.length) return typeof originalUdp === ‘object’ ? JSON.parse(JSON.stringify(originalUdp)) : {};\n const clone = JSON.parse(JSON.stringify(originalUdp));\n clone.documents.forEach((doc, dIdx) => {\n const pages = Array.isArray(doc.pages) ? doc.pages : ;\n pages.forEach((page, pIdx) => {\n if (Array.isArray(page.header)) {\n const existingNames = new Set();\n page.header = page.header.map((h) => {\n const name = h?.name;\n if (!name) return h;\n existingNames.add(name);\n if (Object.prototype.hasOwnProperty.call(data, name)) {\n const isActionField = actionFields && actionFields.has && actionFields.has(name);\n return { \n …h, \n value: String(data[name]),\n …(isActionField ? { isRuleApplied: true, ruleName } : {})\n };\n }\n return h;\n });\n Object.keys(data).forEach(key => {\n if (!existingNames.has(key) && data[key] !== undefined && data[key] !== null) {\n const isActionField = actionFields && actionFields.has && actionFields.has(key);\n page.header.push({\n bbox: [0, 0, 0, 0],\n name: key,\n value: String(data[key]),\n pageNo: page.pageNo ?? (pIdx + 1),\n translated_text: ‘’,\n …(isActionField ? { isRuleApplied: true, ruleName } : {})\n });\n }\n });\n }\n });\n });\n return clone;\n };\n \n // Standalone fetch function using native fetch API\n // const fetchApiData = async (endpoint, authToken, realm) => {\n // try {\n // const headers = {\n // ‘Content-Type’: ‘application/json’\n // };\n \n // if (authToken) {\n // headers[‘Authorization’] = Bearer ${authToken};\n // }\n \n // if (realm) {\n // headers[‘X-Tenant-ID’] = realm;\n // }\n \n // const response = await fetch(endpoint, {\n // method: ‘GET’,\n // headers: headers\n // });\n \n // if (!response.ok) {\n // throw new Error(HTTP error! status: ${response.status});\n // }\n \n // const data = await response.json();\n // return data || {};\n // } catch (error) {\n // console.error(‘Error fetching API data:’, error);\n // return {};\n // }\n // };\n \n const evaluateCondition = (cond, data, apiDataSource = null) => {\n const fieldPath = cond.field || ‘’;\n let value = cond.value;\n const operator = cond.operator;\n\n // Resolve API field references from API data specifically, not UDP data\n if (typeof value === ‘string’ && value.startsWith(‘{API:’) && value.endsWith(‘}’)) {\n const apiField = value.slice(5, -1);\n // Use apiDataSource (matched API record or combined API data) instead of data\n value = apiDataSource?.json?.[apiField];\n }\n\n const parts = String(fieldPath).split(‘.’);\n let current = data;\n for (const p of parts) {\n if (current == null) break;\n current = current[p];\n }\n const actual = current;\n const type = getFieldType(fieldPath);\n\n if (isArrayOperator(operator)) {\n const arr = parseArrayValue(value);\n if (arr.length === 0) return false;\n switch (operator) {\n case ‘in’: return arr.includes(String(actual));\n case ‘notIn’: return !arr.includes(String(actual));\n case ‘containsAny’: return typeof actual === ‘string’ && arr.some(v => String(actual).toLowerCase().includes(String(v).toLowerCase()));\n case ‘containsAll’: return typeof actual === ‘string’ && arr.every(v => String(actual).toLowerCase().includes(String(v).toLowerCase()));\n case ‘between’: {\n if (type === ‘number’) {\n const a = Number(actual), min = Number(arr[0]), max = Number(arr[1]);\n if ([a, min, max].some(Number.isNaN)) return false;\n return a >= min && a <= max;\n }\n if (type === ‘date’) {\n const toDateOnly = (d) => { const dt = new Date(d); return isNaN(dt.getTime()) ? null : new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()).getTime(); };\n const A = toDateOnly(actual), MIN = toDateOnly(arr[0]), MAX = toDateOnly(arr[1]);\n if (A == null || MIN == null || MAX == null) return false;\n return A >= MIN && A <= MAX;\n }\n return false;\n }\n default: return false;\n }\n }\n\n if (type === ‘number’) {\n const a = Number(actual), b = Number(value);\n if (Number.isNaN(a) || Number.isNaN(b)) return false;\n switch (operator) {\n case ‘equals’: return a === b;\n case ‘notEquals’: return a !== b;\n case ‘gt’: return a > b;\n case ‘gte’: return a >= b;\n case ‘lt’: return a < b;\n case ‘lte’: return a <= b;\n default: return false;\n }\n }\n\n if (type === ‘date’) {\n const toDateOnly = (d) => { const dt = new Date(d); return isNaN(dt.getTime()) ? null : new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()).getTime(); };\n const a = toDateOnly(actual), b = toDateOnly(value);\n if (a == null || b == null) return false;\n switch (operator) {\n case ‘equals’: return a === b;\n case ‘notEquals’: return a !== b;\n case ‘gt’: return a > b;\n case ‘gte’: return a >= b;\n case ‘lt’: return a < b;\n case ‘lte’: return a <= b;\n default: return false;\n }\n }\n\n switch (operator) {\n case ‘equals’: return String(actual) === String(value);\n case ‘notEquals’: return String(actual) !== String(value);\n case ‘contains’: return String(actual || ‘’).toLowerCase().includes(String(value).toLowerCase());\n case ‘startsWith’: return String(actual || ‘’).toLowerCase().startsWith(String(value).toLowerCase());\n case ‘endsWith’: return String(actual || ‘’).toLowerCase().endsWith(String(value).toLowerCase());\n case ‘substring’: {\n const parts = String(value).split(‘:’);\n if (parts.length >= 2) {\n const startIndex = parseInt(parts[0]);\n const endIndex = parts.length === 3 ? parseInt(parts[1]) : undefined;\n const substring = parts.length === 3 ? parts[2] : parts[1];\n const actualStr = String(actual || ‘’);\n if (!Number.isNaN(startIndex)) {\n const extracted = endIndex !== undefined ? actualStr.substring(startIndex, endIndex) : actualStr.substring(startIndex);\n return extracted.toLowerCase().includes(substring.toLowerCase());\n }\n }\n return false;\n }\n case ‘length’: {\n const L = parseInt(String(value));\n return !Number.isNaN(L) && String(actual || ‘’).length === L;\n }\n case ‘isEmpty’: return String(actual || ‘’).trim() === ‘’;\n case ‘isNotEmpty’: return String(actual || ‘’).trim() !== ‘’;\n case ‘matches’: {\n try { const regex = new RegExp(String(value), ‘i’); return regex.test(String(actual || ‘’)); } catch { return false; }\n }\n case ‘uppercase’: {\n const s = String(actual || ‘’); return s.length > 0 && s === s.toUpperCase();\n }\n case ‘lowercase’: {\n const s = String(actual || ‘’); return s.length > 0 && s === s.toLowerCase();\n }\n default: return false;\n }\n };\n \n const evaluateGroup = (group, data, apiDataSource = null) => {\n if (!group) return { result: false, actions: };\n const conditionResults = Array.isArray(group.conditions) && group.conditions.length > 0 ? group.conditions.map((c) => evaluateCondition(c, data, apiDataSource)) :;\n const subGroupResults = Array.isArray(group.subGroups) && group.subGroups.length > 0 ? group.subGroups.map((sg) => evaluateGroup(sg, data, apiDataSource)) : ;\n const allResults = […conditionResults, …subGroupResults.map((sg) => sg.result)];\n const groupResult = allResults.length === 0 ? false : (group.logic === ‘OR’ ? allResults.some(Boolean) : allResults.every(Boolean));\n const allActions = ;\n if (groupResult && Array.isArray(group.actions) && group.actions.length > 0) {\n allActions.push(…group.actions.map((action) => ({ …action, level: group.level, groupId: group.id })));\n }\n subGroupResults.forEach((sg) => { if (sg.result && sg.actions.length > 0) allActions.push(…sg.actions); });\n return { result: groupResult, actions: allActions };\n };\n \n const evaluateRules = (rule, data, apiDataSource = null) => {\n if (!rule || !Array.isArray(rule.groups)) return { result: false, actions: };\n const groupResults = rule.groups.map((g) => evaluateGroup(g, data, apiDataSource));\n const ruleResult = rule.logic === ‘OR’ ? groupResults.some(gr => gr.result) : groupResults.every(gr => gr.result);\n const allActions =;\n groupResults.forEach(gr => { if (gr.result && gr.actions.length > 0) allActions.push(…gr.actions); });\n if (ruleResult && Array.isArray(rule.actions) && rule.actions.length > 0) {\n allActions.push(…rule.actions.map((action) => ({ …action, level: -1, groupId: ‘rule’ })));\n }\n return { result: ruleResult, actions: allActions };\n };\n \n // ========== MAIN PROCESSING LOGIC ==========\n \n // Parse UDP input\n const udpOriginal = typeof udpInput === ‘string’ ? JSON.parse(udpInput) : udpInput;\n const flatInput = convertUdpToFlatData(udpOriginal);\n\n // Fetch API data if endpoint is configured\n let apiData = {};\n if (rule.apiConfig?.endpoint) {\n try {\n apiData = apiData1;\n console.log(‘in apiData1’, apiData);\n } catch (error) {\n console.warn(‘Failed to fetch API data, proceeding without it:’, error);\n }\n }\n\n // Handle API data - if it’s an array, find matching record(s)\n let matchedApiRecord = null;\n let combinedData = { …flatInput };\n\n if (Array.isArray(apiData) && apiData.length > 0) {\n // Try to find a matching record in the API array\n // API data comes under json key\n for (const record of apiData) {\n const recordData = record?.json || record;\n const testData = { …flatInput, …recordData };\n const testEval = evaluateRules(rule, testData, record);\n if (testEval.result) {\n matchedApiRecord = record;\n combinedData = testData;\n break;\n }\n }\n \n // If no match found, still combine with first record for API field resolution\n if (!matchedApiRecord && apiData.length > 0) {\n const firstRecordData = apiData[0]?.json || apiData[0];\n combinedData = { …flatInput, …firstRecordData };\n }\n } else if (typeof apiData === ‘object’ && !Array.isArray(apiData)) {\n // If apiData is a single object, merge it directly\n // API data comes under json key\n const apiDataJson = apiData?.json || apiData;\n combinedData = { …flatInput, …apiDataJson };\n }\n\n // Determine the API data source for evaluation\n const apiDataForEvaluation = matchedApiRecord || (Array.isArray(apiData) && apiData.length > 0 ? apiData[0] : apiData);\n\n // Evaluate rules against combined data, passing API data for {API:} placeholder resolution\n const evaluation = evaluateRules(rule, combinedData, apiDataForEvaluation);\n const flatResult = { …flatInput };\n const codeErrors = ;\n\n // Helper function to apply regex conversion to a value\n const applyRegexConversion = (value, pattern, replacement) => {\n if (!pattern || !value) return value;\n try {\n const regex = new RegExp(pattern);\n const strValue = String(value);\n \n if (replacement !== undefined && replacement !== null && replacement !== ‘’) {\n // Replace using the replacement pattern (supports $1, $2, etc. for capturing groups)\n // Use a function to handle replacement to ensure $1, $2, etc. work correctly\n const result = strValue.replace(regex, (match, …groups) => {\n // Replace $1, $2, etc. in the replacement string with actual captured groups\n let repl = String(replacement);\n for (let i = 0; i < groups.length; i++) {\n const groupIndex = i + 1;\n const groupValue = groups[i] !== undefined ? groups[i] : ‘’;\n // Replace $1, $2, etc. with actual group values\n repl = repl.replace(new RegExp(\\\\$${groupIndex}, ‘g’), groupValue);\n // Also handle $& (full match), $$ (literal $)\n repl = repl.replace(/\$\&/g, match);\n repl = repl.replace(/\$\$/g, ‘$’);\n }\n return repl;\n });\n return result;\n } else {\n // Extract matched value (no replacement pattern provided)\n const match = strValue.match(regex);\n if (!match) return value;\n \n // If there are capturing groups, return the first capturing group (match[1])\n // Otherwise, return the full match (match[0])\n // match[1] is the first capturing group, match[0] is the full match\n return match.length > 1 && match[1] !== undefined ? match[1] : match[0];\n }\n } catch (error) {\n // If regex is invalid, return original value\n console.warn(‘Invalid regex pattern in action:’, pattern, error);\n return value;\n }\n };\n\n const executeCodeBlock = (code, allowedVariablesData, apiFieldsData) => {\n if (!code || !code.trim()) {\n return { value: undefined };\n }\n try {\n const runner = new Function(\n ‘allowedVariables’,\n ‘apiFields’,\n \n \"use strict\";\n ${code}\n if (typeof main !== 'function') {\n throw new Error('main function not found. Please define function main(allowedVariables, apiFields)');\n }\n return main(allowedVariables, apiFields);\n \n );\n const value = runner(allowedVariablesData || {}, apiFieldsData || {});\n return { value };\n } catch (error) {\n const message = error instanceof Error ? error.message : ‘Unknown error executing code block’;\n console.error(‘Error executing code block:’, error);\n return { value: undefined, error: message };\n }\n };\n\n const readApiFieldValue = (apiSource, field) => {\n if (!apiSource || typeof apiSource !== ‘object’) return undefined;\n if (Object.prototype.hasOwnProperty.call(apiSource, field)) {\n return apiSource[field];\n }\n if (apiSource.json && Object.prototype.hasOwnProperty.call(apiSource.json, field)) {\n return apiSource.json[field];\n }\n return undefined;\n };\n\n const resolveActionValue = (action, dataSource, apiSource) => {\n if (!action) return { value: undefined };\n\n if (action.useCodeBlock && action.codeBlock?.code) {\n return executeCodeBlock(action.codeBlock.code, dataSource || {}, apiSource);\n }\n\n let actionValue = action.value;\n\n if (typeof actionValue === ‘string’ && actionValue.startsWith(‘{API:’) && actionValue.endsWith(‘}’)) {\n const apiField = actionValue.slice(5, -1);\n const apiVal = readApiFieldValue(apiSource, apiField);\n if (apiVal !== undefined) {\n actionValue = apiVal;\n } else if (dataSource && Object.prototype.hasOwnProperty.call(dataSource, apiField)) {\n actionValue = dataSource[apiField];\n } else {\n actionValue = [API:${apiField}];\n }\n }\n\n if (\n typeof actionValue === ‘string’ &&\n !actionValue.startsWith(‘{API:’) &&\n dataSource &&\n Object.prototype.hasOwnProperty.call(dataSource, actionValue)\n ) {\n actionValue = dataSource[actionValue];\n }\n\n if (action.useRegex && action.regexPattern) {\n let sourceValue = actionValue;\n if (\n (sourceValue === undefined || sourceValue === ‘’) &&\n dataSource &&\n Object.prototype.hasOwnProperty.call(dataSource, action.field)\n ) {\n sourceValue = dataSource[action.field];\n }\n actionValue = applyRegexConversion(sourceValue, action.regexPattern, action.regexReplacement);\n }\n\n return { value: actionValue };\n };\n\n const applyValueToData = (target, path, value) => {\n if (!target || !path) return;\n const segments = String(path).split(‘.’);\n if (segments.length === 1) {\n target[segments[0]] = value;\n return;\n }\n const [root, …rest] = segments;\n target[root] = { …(target[root] || {}) };\n let cursor = target[root];\n for (let i = 0; i < rest.length - 1; i++) {\n const key = rest[i];\n cursor[key] = { …(cursor[key] || {}) };\n cursor = cursor[key];\n }\n cursor[rest[rest.length - 1]] = value;\n };\n\n const applyActions = (actions, targetData, lookupSource, apiSource, errorCollector) => {\n actions.forEach((action, index) => {\n if (!action?.field) return;\n const { value, error } = resolveActionValue(action, lookupSource || targetData, apiSource);\n if (error) {\n errorCollector.push(${action.field || Action ${index + 1}}: ${error});\n return;\n }\n if (value === undefined) return;\n applyValueToData(targetData, action.field, value);\n });\n };\n\n // Apply actions if rule matches\n if (evaluation.result && evaluation.actions?.length) {\n applyActions(evaluation.actions, flatResult, combinedData, apiDataForEvaluation, codeErrors);\n }\n\n // Build UDP result from the modified flat data\n const actionFieldsSet = new Set(\n (evaluation.actions || )\n .map((action) => action.field)\n .filter((field) => field)\n );\n const udpResult = buildUdpFromData(flatResult, udpOriginal, actionFieldsSet, rule.name);\n \n return {\n matched: evaluation.result,\n actions: evaluation.actions,\n flatResult,\n udpResult,\n flatInput,\n apiData: Object.keys(apiData).length > 0 ? apiData : undefined,\n codeErrors\n };\n}\n\nvar extractedJson = JSON.parse($(‘Get case UDP’).first().json.extractedJson)\nlet apiData = ;\n\ntry {\n apiData = $("API-Data").all();\n} catch (error) {\n // Node API-Data hasn’t run or no data available\n apiData = ;\n}\nreturn processRuleWithUdp($(‘Rule JSON’).first().json.rule, extractedJson, apiData);”},“type”:“n8n-nodes-base.code”,“typeVersion”:2,“position”:[992,-144],“id”:“be7ab5dd-c77c-4f09-9aa4-6383a78fc6f3”,“name”:“Rule Apply”},{“parameters”:{“jsCode”:“return {output: JSON.stringify(JSON.stringify($input.first().json.udpResult))};”},“type”:“n8n-nodes-base.code”,“typeVersion”:2,“position”:[1264,-256],“id”:“a8fd1878-adf7-436c-982d-53e96369da1b”,“name”:“Transformation”},{“parameters”:{“method”:“POST”,“url”:“``https://saas-uat-kc.saas.sapper.ai/realms/genpact/protocol/openid-connect/token",“authentication”:“genericCredentialType”,“genericAuthType”:“httpBearerAuth”,“sendHeaders”:true,“headerParameters”:{“parameters”:[{“name”:“Content-Type”,“value”:“application/x-www-form-urlencoded”}]},“sendBody”:true,“bodyParameters”:{“parameters”:[{“name”:“username”,“value”:“john.son”},{“name”:“password”,“value”:“sdsd@asA!21”},{“name”:“grant_type”,“value”:“password”},{“name”:“client_id”,“value”:“finance-ai”}]},“options”:{}},“type”:“n8n-nodes-base.httpRequest”,“typeVersion”:4.3,“position”:[-400,-64],“id”:“c17a5487-ec17-40f9-867c-2904c478700c”,“name”:“Authentication”,“credentials”:{“httpBearerAuth”:{“id”:“3G2AEeAwhoc3Rgyw”,“name”:"Bearer`` Auth account”}}},{“parameters”:{“url”:“=``https://finance-uat.saas.sapper.ai/apApp/api/ap-case-extractions?caseId.equals=``{{ $(‘Webhook’).item.json.body.caseId }}”,“sendHeaders”:true,“headerParameters”:{“parameters”:[{“name”:“Authorization”,“value”:“=Bearer {{ $(‘Authentication’).item.json.access_token }}”},{“name”:“X-Tenant-Id”,“value”:“={{ $(‘Webhook’).item.json.headers["x-tenant-id"] }}”}]},“options”:{}},“type”:“n8n-nodes-base.httpRequest”,“typeVersion”:4.3,“position”:[352,-48],“id”:“3aed10e3-adec-44f2-b9ef-2f34d2893d9a”,“name”:“Get case UDP”},{“parameters”:{“url”:“``https://finance-uat.saas.sapper.ai/apApp/api/rule-definitions?ruleType.equals=POST_EXTRACTION_RULE&isActive.equals=true",“sendHeaders”:true,“headerParameters”:{“parameters”:[{“name”:“Authorization”,“value”:"=Bearer`` {{ $(‘Authentication’).item.json.access_token }}”},{“name”:“X-Tenant-ID”,“value”:“={{ $(‘Webhook’).item.json.headers["x-tenant-id"] }}”}]},“options”:{}},“type”:“n8n-nodes-base.httpRequest”,“typeVersion”:4.3,“position”:[-144,-32],“id”:“8ff16302-4c4f-434c-a12d-027c6fef0247”,“name”:“Get POST_EXtarction_Rule”},{“parameters”:{“method”:“PUT”,“url”:“=``https://finance-uat.saas.sapper.ai/apApp/api/ap-case-extractions/V2/``{{ $(‘Get case UDP’).first().json.id }}”,“sendHeaders”:true,“headerParameters”:{“parameters”:[{“name”:“Authorization”,“value”:“=Bearer {{ $(‘Authentication’).first().json.access_token }}”},{“name”:“X-Tenant-ID”,“value”:“={{ $(‘Webhook’).first().json.headers["x-tenant-id"] }}”},{“name”:“X-Company-ID”,“value”:“={{ $(‘Webhook’).first().json.headers["x-company-id"] }}”}]},“sendBody”:true,“specifyBody”:“json”,“jsonBody”:“={\n "id": {{ $(‘Get case UDP’).first().json.id }},\n "caseId": "{{ $(‘Get case UDP’).first().json.caseId }}",\n "extractedJson": {{ $(‘Transformation’).item.json.output }},\n "documentName": "{{ $(‘Get case UDP’).first().json.documentName }}",\n"companyId": "{{ $(‘Webhook’).first().json.headers["x-company-id"] }}",\n"wordListJson": {{ $json.output }}\n}”,“options”:{}},“type”:“n8n-nodes-base.httpRequest”,“typeVersion”:4.3,“position”:[1568,-64],“id”:“2d441747-edbc-4838-b96d-bb9307e9fba3”,“name”:“Update UDP”},{“parameters”:{“method”:“POST”,“url”:“``http://kafka-connector.n8n:8080/send",“sendBody”:true,“specifyBody”:“json”,“jsonBody”:"=``{ \n "kafka_topic":"ap.uat.rule.out",\n "key": "",\n "message_content": { \n "case_id": "{{ $(‘Get case UDP’).first().json.caseId }}",\n "Message":5\n}\n} “,“options”:{}},“type”:“n8n-nodes-base.httpRequest”,“typeVersion”:4.3,“position”:[1792,-64],“id”:“6e011280-00d6-4039-8cc8-ee1e2a00ab21”,“name”:“HTTP Request”},{“parameters”:{“conditions”:{“options”:{“caseSensitive”:true,“leftValue”:””,“typeValidation”:“strict”,“version”:2},“conditions”:[{“id”:“e3377a3b-d833-43c7-8928-412dfa8d5bb8”,“leftValue”:“={{ $json.rule.apiConfig }}”,“rightValue”:“”,“operator”:{“type”:“object”,“operation”:“notEmpty”,“singleValue”:true}},{“id”:“700499a4-66e0-4037-8b5f-37762e216078”,“leftValue”:“={{ $json.rule.apiConfig.endpoint }}”,“rightValue”:“”,“operator”:{“type”:“string”,“operation”:“notEmpty”,“singleValue”:true}}],“combinator”:“and”},“options”:{}},“type”:“n8n-nodes-base.if”,“typeVersion”:2.2,“position”:[592,-64],“id”:“346af75e-f473-40d9-bbb1-2dd5e5011d7e”,“name”:“If”},{“parameters”:{“url”:“={{ $json.rule.apiConfig.endpoint }}”,“sendHeaders”:true,“headerParameters”:{“parameters”:[{“name”:“Authorization”,“value”:“=Bearer {{ $(‘Authentication’).item.json.access_token }}”},{“name”:“X-Tenant-ID”,“value”:“={{ $(‘Webhook’).item.json.headers["x-tenant-id"] }}”}]},“options”:{}},“type”:“n8n-nodes-base.httpRequest”,“typeVersion”:4.3,“position”:[816,-336],“id”:“2b42abd0-bb2d-42e1-b287-5a79396aeaa9”,“name”:“API-Data”},{“parameters”:{“options”:{}},“type”:“n8n-nodes-base.splitInBatches”,“typeVersion”:3,“position”:[112,-128],“id”:“2f626dae-3610-4cc7-a5d4-eb158a3f7d8d”,“name”:“Loop Over Items”},{“parameters”:{“jsCode”:“let wordListJson = $(‘Get case UDP’).first().json.wordListJson;\n\n// Check if wordListJson is a valid JSON object (not string, null, or primitive)\nlet output;\nif (wordListJson && typeof wordListJson === ‘object’ && !Array.isArray(wordListJson)) {\n // It’s a JSON object - stringify it\n output = JSON.stringify(wordListJson);\n} else {\n // It’s already a string, null, array, or primitive - send as is\n output = wordListJson;\n}\n\nreturn { output: JSON.stringify(output) };”},“type”:“n8n-nodes-base.code”,“typeVersion”:2,“position”:[1344,-64],“id”:“00ac67ef-0d5c-4c3e-9a6b-8d10763e12d7”,“name”:“Wordlist transformation”}],“pinData”:{“Webhook”:[{“json”:{“headers”:{“host”:“n8n.saas.sapper.ai”,“x-request-id”:“e145b2fb2d69018f2009e8a02cfda31a”,“x-real-ip”:“10.1.141.44”,“x-forwarded-for”:“10.1.141.44”,“x-forwarded-host”:“n8n.saas.sapper.ai”,“x-forwarded-port”:“80”,“x-forwarded-proto”:“http”,“x-forwarded-scheme”:“http”,“x-scheme”:“http”,“x-original-forwarded-for”:“10.1.173.82”,“content-length”:“49”,“x-amzn-trace-id”:“Root=1-693989ba-506ce7f532a9dc0d56d59c4c”,“content-type”:“application/json”,“x-tenant-id”:“genpact”,“x-company-id”:“CISEA64416600”,“authorization”:“Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMbzE2Qks3SGYyR1hLZ25OX1hnWXlXekJpd1Bmd196VWJJcUI3V1E2dFRFIn0.eyJleHAiOjE3NjUzNzg3OTAsImlhdCI6MTc2NTM3ODQ5MCwianRpIjoiMGI5YTZmOWItMjY5OS00M2RjLTkwNjgtYzllMTBmZDk3ZTdlIiwiaXNzIjoiaHR0cHM6Ly9zYWFzLXVhdC1rYy5zYWFzLnNhcHBlci5haS9yZWFsbXMvbWFzdGVyIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWRtaW4tY2xpIiwic2lkIjoiMDE3OTIyNTktNGNmZS00Y2UzLWIyMjktZjY2Yjc0NWExMzI0Iiwic2NvcGUiOiJlbWFpbCBwcm9maWxlIn0.gYoVCHfFgPciwPyPy6GUh8kwymQmofVDi2GpoY_Ndz8hVZEVsibRWABRyD4AXR9hBKWA_GpdgyUtq5JymXHJvA8TkI_d6j1mhXFu89DHi8Fzw_3xjkYcxhpOev-_rvSvHgJEevSsly8RJrbOJnhPBzpGDYCFYjcUGBfBXXsRprJggYuu6TqR3D-4jqxrll0g_UNcoVSySndrAA2_Z0oDLVbOgg5_9l58dpexuXMzpthaVhcAJ65amMqoVhiKVOZz1cgIoMc-4hk4Qs_5e8bVNt_cqw3WDozcdBOVh95Kgxy4TReV-vBPXHNbMcPMm59zcoZiSimQx4LyNCgy2DL4jg”,“user-agent”:“unirest-java/3.1.00”,“accept-encoding”:“gzip”},“params”:{},“query”:{},“body”:{“caseId”:“d433c884-fb83-4de3-a508-e2123d43945e”},“webhookUrl”:“``http://localhost:5678/webhook/e63e5a9d-4c3f-4677-a496-02c62e955e0c",“executionMode”:“production”},“pairedItem”:{“item”:0}}]},“connections”:{“Webhook”:{“main”:[[{“node”:“Authentication”,“type”:“main”,“index”:0}]]},"Rule`` JSON”:{“main”:[[{“node”:“If”,“type”:“main”,“index”:0}]]},“Rule Apply”:{“main”:[[{“node”:“Transformation”,“type”:“main”,“index”:0}]]},“Transformation”:{“main”:[[{“node”:“Wordlist transformation”,“type”:“main”,“index”:0}]]},“Authentication”:{“main”:[[{“node”:“Get POST_EXtarction_Rule”,“type”:“main”,“index”:0}]]},“Get case UDP”:{“main”:[[{“node”:“Rule JSON”,“type”:“main”,“index”:0}]]},“Get POST_EXtarction_Rule”:{“main”:[[{“node”:“Loop Over Items”,“type”:“main”,“index”:0}]]},“Update UDP”:{“main”:[[{“node”:“HTTP Request”,“type”:“main”,“index”:0}]]},“If”:{“main”:[[{“node”:“API-Data”,“type”:“main”,“index”:0}],[{“node”:“Rule Apply”,“type”:“main”,“index”:0}]]},“API-Data”:{“main”:[[{“node”:“Rule Apply”,“type”:“main”,“index”:0}]]},“Loop Over Items”:{“main”:[,[{“node”:“Get case UDP”,“type”:“main”,“index”:0}]]},“HTTP Request”:{“main”:[[{“node”:“Loop Over Items”,“type”:“main”,“index”:0}]]},“Wordlist transformation”:{“main”:[[{“node”:“Update UDP”,“type”:“main”,“index”:0}]]}},“active”:true,“settings”:{“executionOrder”:“v1”},“versionId”:“782310df-afe9-4e2b-99da-af92347c5142”,“meta”:{“templateCredsSetupCompleted”:true,“instanceId”:“412c854216d86882260dd591aa70a6a765e415b0f7d725e9044253f8d5d2eec3”},“id”:“8MrBTL6wDfjqjaG1”,“tags”:}