From 2f5c19c20686e05dece342a694963cbc332124d7 Mon Sep 17 00:00:00 2001 From: bill Date: Sun, 2 Nov 2025 03:26:03 +0000 Subject: [PATCH] Add Enrich Client Excel from Email.json --- Enrich Client Excel from Email.json | 901 ++++++++++++++++++++++++++++ 1 file changed, 901 insertions(+) create mode 100644 Enrich Client Excel from Email.json diff --git a/Enrich Client Excel from Email.json b/Enrich Client Excel from Email.json new file mode 100644 index 0000000..d2925c0 --- /dev/null +++ b/Enrich Client Excel from Email.json @@ -0,0 +1,901 @@ +{ + "createdAt": "2025-11-01T05:48:54.245Z", + "updatedAt": "2025-11-01T05:48:54.245Z", + "id": "sMcGBE1qq5fln6EM", + "name": "Enrich Client Excel from Email", + "active": false, + "isArchived": false, + "nodes": [ + { + "parameters": {}, + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 0, + 0 + ], + "id": "a96f3a61-9bbb-47cb-a53c-2c42c5fe4d64", + "name": "When clicking ‘Execute workflow’" + }, + { + "parameters": { + "options": { + "systemMessage": "You enrich missing client fields from a single email.\n\nYou are given:\n- email: { from, to, cc, bcc, subject, body }\n- clients: [ { id, name, company, address, city, state, zip, phone, cell, fax, email, projects, last, followUp }, ... ]\n\nYour job:\n1) Identify which client(s) the email refers to via name/email/signature/routing or explicit textual cues.\n2) Extract ONLY fields that are currently missing/blank in the matched client(s). Do not overwrite any non-empty value.\n3) If there is reference to a job or project those should be appended and added under \"projects\"\n4) If there was a request for some type of follow up that should be noted under \"followUp\"\n5) Normalize:\n - email: lowercase, trimmed\n - phone/cell/fax: digits+dashes; e.g., 832-691-6430\n - zip: 5 digits (or 9 with dash if present)\n - dates (e.g., \"last\"): YYYY-MM-DD\n6) separate out full addresses into their component parts (e.g., street separate from city, separate from state, etc.)\n7) Note that most of the time the persons e-mail address is actually in the to, from, cc or bcc data elements. Be sure to capture those.\n8) If ambiguous, skip that field—do not guess.\n\nSTRICT OUTPUT (no commentary):\nReturn a single JSON object:\n{\n \"updates\": [\n {\n \"name\": \"John Doe\",\n \"id\": \"\",\n // include only fields you actually found and that were previously missing\n \"email\": \"name@example.com\",\n \"phone\": \"832-691-6430\",\n \"address\": \"123 Main St\",\n \"city\": \"Olalla\",\n \"state\": \"WA\",\n \"zip\": \"98359\",\n \"cell\": \"832-555-1212\",\n \"fax\": \"555-1212\",\n \"company\": \"Acme Co\",\n \"projects\": \"Kitchen Remodel\",\n \"last\": \"2025-10-28\",\n \"followUp\": \"Call next week\"\n }\n ]\n}\n\nRules:\n- If nothing to update, return { \"updates\": [] }.\n- Do NOT include the full client list in the output.\n- Output JSON only (no code fences, no prose).\n", + "enableStreaming": true + } + }, + "type": "@n8n/n8n-nodes-langchain.agent", + "typeVersion": 2.2, + "position": [ + 2112, + 160 + ], + "id": "4f1fde5e-f803-4773-947b-2e56761dc942", + "name": "AI Agent1" + }, + { + "parameters": { + "model": { + "__rl": true, + "mode": "list", + "value": "gpt-4.1-mini" + }, + "options": {} + }, + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1.2, + "position": [ + 2064, + 384 + ], + "id": "a4da7f57-763c-4a02-9162-87cdef00cc46", + "name": "OpenAI Chat Model1" + }, + { + "parameters": {}, + "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow", + "typeVersion": 1.3, + "position": [ + 2256, + 384 + ], + "id": "fd56efeb-4b9b-42d8-b753-051a865fc380", + "name": "Simple Memory1" + }, + { + "parameters": { + "operation": "xlsx", + "options": { + "headerRow": true, + "includeEmptyCells": true, + "rawData": true + } + }, + "type": "n8n-nodes-base.extractFromFile", + "typeVersion": 1, + "position": [ + 592, + 0 + ], + "id": "48ce1be0-3249-4ea4-a4fc-136d7f7c9413", + "name": "formatClientList", + "alwaysOutputData": true + }, + { + "parameters": { + "fileSelector": "/data/bill/clientList.xlsx", + "options": {} + }, + "type": "n8n-nodes-base.readWriteFile", + "typeVersion": 1, + "position": [ + 384, + 0 + ], + "id": "e2667bd1-2ba9-45d4-8ce2-ddd512926541", + "name": "importClientList" + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "b73ca669-5c9b-4a23-ab78-d1a111c8f129", + "name": "sessionId", + "value": "={{ $now }}", + "type": "string" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [ + 176, + 0 + ], + "id": "a505f8e2-21e1-421c-8943-9b7511f0b616", + "name": "sessionIdCreate" + }, + { + "parameters": { + "dataTableId": { + "__rl": true, + "value": "RE5GmewdBpOuPS4E", + "mode": "list", + "cachedResultName": "clientList", + "cachedResultUrl": "/projects/MAbDV0ZMuTHgonhb/datatables/RE5GmewdBpOuPS4E" + }, + "columns": { + "mappingMode": "autoMapInputData", + "value": { + "name": "={{ $json.name }}" + }, + "matchingColumns": [], + "schema": [ + { + "id": "name", + "displayName": "name", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "company", + "displayName": "company", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "address", + "displayName": "address", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "city", + "displayName": "city", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "state", + "displayName": "state", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "zip", + "displayName": "zip", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "phone", + "displayName": "phone", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "cell", + "displayName": "cell", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "fax", + "displayName": "fax", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "email", + "displayName": "email", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "projects", + "displayName": "projects", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "last", + "displayName": "last", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "followUp", + "displayName": "followUp", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + } + ], + "attemptToConvertTypes": false, + "convertFieldsToString": false + }, + "options": {} + }, + "type": "n8n-nodes-base.dataTable", + "typeVersion": 1, + "position": [ + 1088, + 144 + ], + "id": "37663988-fdc0-4dbc-8900-761186658c6e", + "name": "createClientDataTable" + }, + { + "parameters": { + "fileSelector": "/data/bill/Inbox", + "options": {} + }, + "type": "n8n-nodes-base.readWriteFile", + "typeVersion": 1, + "position": [ + 1264, + 144 + ], + "id": "804c8f42-0b5a-4c83-b5bf-f606b47883f4", + "name": "importEmailBinary" + }, + { + "parameters": { + "jsCode": "// Access base64 string from binary.data.data\nconst base64Str = $input.first().binary?.data?.data;\n\nif (!base64Str) {\n throw new Error('No base64 string found in binary data');\n}\n\n// Decode base64 to UTF-8 text\nconst buffer = Buffer.from(base64Str, 'base64');\nconst raw = buffer.toString('utf8');\n\n// Split into individual emails by mbox-style delimiter\nconst rawEmails = raw.split(/\\n(?=From - )/);\n\n// Utility to extract headers\nfunction extractHeader(line, field) {\n const regex = new RegExp(`^${field}: (.+)`, 'i');\n const match = line.match(regex);\n return match ? match[1].trim() : null;\n}\n\n// Parse each email block\nconst parsed = rawEmails.map(block => {\n const lines = block.split('\\n');\n const headers = {\n from: null,\n to: null,\n cc: null,\n bcc: null,\n subject: null,\n body: '',\n };\n\n let isHeader = true;\n const bodyLines = [];\n\n for (let line of lines) {\n if (isHeader) {\n if (line.trim() === '') {\n isHeader = false;\n continue;\n }\n headers.from = headers.from || extractHeader(line, 'From');\n headers.to = headers.to || extractHeader(line, 'To');\n headers.cc = headers.cc || extractHeader(line, 'Cc');\n headers.bcc = headers.bcc || extractHeader(line, 'Bcc');\n headers.subject = headers.subject || extractHeader(line, 'Subject');\n } else {\n bodyLines.push(line);\n }\n }\n\n headers.body = bodyLines.join('\\n').trim();\n return { json: headers };\n});\n\nreturn parsed;\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1440, + 144 + ], + "id": "8d6eca1e-e5bc-49a4-af6d-2d25bf373490", + "name": "createEmailString" + }, + { + "parameters": { + "options": {} + }, + "type": "n8n-nodes-base.splitInBatches", + "typeVersion": 3, + "position": [ + 1648, + 144 + ], + "id": "adaec926-df6f-46dc-b06d-5d033710d1e9", + "name": "loopOverEmail" + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "0c87872b-222c-47f0-94c8-84d0692e03ff", + "name": "sessionId", + "value": "={{ $('sessionIdCreate').first().json.sessionId }}", + "type": "string" + }, + { + "id": "6c9d3fd3-1405-4422-a47b-89b356efa616", + "name": "chatInput", + "value": "={{ JSON.stringify({\n instruction: \"You are a data assistant. Given a single email and a list of clients with potentially incomplete records, scan the email to find any client it refers to. If you find a match, extract any missing information and return a list of only the updated fields. Do not change any existing data.\",\n email: {\n from: $json.from,\n to: $json.to,\n cc: $json.cc,\n bcc: $json.bcc,\n subject: $json.subject,\n body: $json.body\n },\n clients: $('formatClientList').all().map(client => ({\n id: client.json.id ?? client.itemIndex + 1,\n ...client.json\n }))\n}) }}\n", + "type": "string" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [ + 1904, + 160 + ], + "id": "0c27a0df-6313-43aa-9381-135de87eeaae", + "name": "createPrompt" + }, + { + "parameters": { + "jsCode": "// ---- helpers ----\nfunction normalizePhone(v){ if (typeof v !== 'string') return v; const d=v.replace(/\\D+/g,''); if(d.length===10)return`${d.slice(0,3)}-${d.slice(3,6)}-${d.slice(6)}`; if(d.length===11&&d.startsWith('1'))return`${d.slice(1,4)}-${d.slice(4,7)}-${d.slice(7)}`; return String(v).trim(); }\nconst normalizeEmail = v => (typeof v === 'string' ? v.trim().toLowerCase() : v);\nfunction normalizeZip(v){ if(v==null)return v; const s=String(v).trim(); const m=s.match(/^(\\d{5})(\\d{4})$/); if(m)return`${m[1]}-${m[2]}`; if(/^\\d{5}$/.test(s))return s; const x=s.match(/^(\\d{5})[- ](\\d{4})$/); if(x)return`${x[1]}-${x[2]}`; return s; }\nfunction normalizeDateYMD(v){ if(!v)return v; const s=String(v).trim(); if(/^\\d{4}-\\d{2}-\\d{2}$/.test(s))return s; const d=new Date(s); if(!isNaN(d.valueOf())){ const y=d.getUTCFullYear(); const m=String(d.getUTCMonth()+1).padStart(2,'0'); const day=String(d.getUTCDate()).padStart(2,'0'); return `${y}-${m}-${day}`;} return s; }\nconst isBlank = v => v === '' || v === null || typeof v === 'undefined';\nconst allowed = new Set(['id','name','company','address','city','state','zip','phone','cell','fax','email','projects','last','followUp']);\n\n// ---- get BOTH inputs safely ----\nlet inputs = $input.all().filter(arr => Array.isArray(arr) && arr.length > 0);\n\n// Identify which input is the updates bundle and which is the table rows\nlet updatesArr = [];\nlet tableRows = [];\n\nfor (const arr of inputs) {\n const j = arr[0]?.json ?? {};\n if (Array.isArray(j.updates)) {\n updatesArr = j.updates;\n } else if (j && (j.id !== undefined || j.name !== undefined)) {\n // Looks like a client row\n tableRows = arr.map(i => ({ ...i.json }));\n }\n}\n\n// If your n8n wiring is reversed, fall back by position\nif (updatesArr.length === 0 && inputs[0]?.[0]?.json?.updates) {\n updatesArr = inputs[0][0].json.updates;\n}\nif (tableRows.length === 0 && inputs[1]?.length) {\n tableRows = inputs[1].map(i => ({ ...i.json }));\n}\n\n// If still nothing, just pass through\nif (tableRows.length === 0) {\n console.log('No table rows arrived; passing nothing.');\n return [];\n}\n\n// Build id -> row map\nconst byId = new Map();\nfor (const row of tableRows) {\n const id = String(row.id ?? row.ID ?? row.Id ?? '');\n if (id) byId.set(id, row);\n}\n\n// Apply updates (fill only blanks)\nfor (const u of updatesArr) {\n if (!u || u.id === undefined || u.id === null) continue;\n const id = String(u.id);\n const row = byId.get(id);\n if (!row) continue;\n\n for (const [k, vRaw] of Object.entries(u)) {\n if (k === 'id' || !allowed.has(k)) continue;\n if (isBlank(vRaw) || !isBlank(row[k])) continue;\n\n let v = vRaw;\n if (k === 'email') v = normalizeEmail(vRaw);\n if (k === 'phone' || k === 'cell' || k === 'fax') v = normalizePhone(vRaw);\n if (k === 'zip') v = normalizeZip(vRaw);\n if (k === 'last') v = normalizeDateYMD(vRaw);\n\n row[k] = v;\n }\n}\n\n// Emit updated rows for Upsert\nreturn tableRows.map(r => ({ json: r }));\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2880, + 160 + ], + "id": "f50ea076-1b07-4367-af7f-b4cc0c0f9fd1", + "name": "Parse & Apply Client Updates", + "alwaysOutputData": true + }, + { + "parameters": { + "jsCode": "// AI Agent1 puts JSON as a STRING in `output`.\n// Parse it here and emit a clean object with { updates: [...] }\n\nconst raw = $json.output ?? '';\nlet updates = [];\n\nif (typeof raw === 'string') {\n try {\n const obj = JSON.parse(raw);\n if (obj && Array.isArray(obj.updates)) updates = obj.updates;\n } catch (e) {\n // leave updates as []\n }\n} else if (raw && Array.isArray(raw.updates)) {\n updates = raw.updates; // already an object (just in case)\n}\n\n// For visibility during testing\nconsole.log('Extracted updates:', updates);\n\nreturn [{ json: { updates } }];\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2464, + 160 + ], + "id": "6a834e6e-d437-4d7a-a6fb-c9f57791ba15", + "name": "Extract Updates" + }, + { + "parameters": { + "operation": "get", + "dataTableId": { + "__rl": true, + "value": "RE5GmewdBpOuPS4E", + "mode": "list", + "cachedResultName": "clientList", + "cachedResultUrl": "/projects/MAbDV0ZMuTHgonhb/datatables/RE5GmewdBpOuPS4E" + }, + "filters": { + "conditions": [ + { + "keyValue": "={{ $json.updates[0].id }}" + } + ] + }, + "returnAll": true + }, + "type": "n8n-nodes-base.dataTable", + "typeVersion": 1, + "position": [ + 2672, + 160 + ], + "id": "d80698d9-963d-4c92-8c54-16f39057af4d", + "name": "Get row(s)", + "alwaysOutputData": true + }, + { + "parameters": { + "operation": "update", + "dataTableId": { + "__rl": true, + "value": "RE5GmewdBpOuPS4E", + "mode": "list", + "cachedResultName": "clientList", + "cachedResultUrl": "/projects/MAbDV0ZMuTHgonhb/datatables/RE5GmewdBpOuPS4E" + }, + "matchType": "allConditions", + "filters": { + "conditions": [ + { + "keyName": "name", + "keyValue": "={{ $('Extract Updates').item.json.updates[0].name }}" + }, + { + "keyValue": "={{ $('Extract Updates').item.json.updates[0].id }}" + } + ] + }, + "columns": { + "mappingMode": "defineBelow", + "value": { + "email": "={{ $('Extract Updates').item.json.updates[0].email }}", + "company": "={{ $('Extract Updates').item.json.updates[0].company }}", + "address": "={{ $('Extract Updates').item.json.updates[0].address }}", + "city": "={{ $('Extract Updates').item.json.updates[0].city }}", + "state": "={{ $('Extract Updates').item.json.updates[0].state }}", + "zip": "={{ $('Extract Updates').item.json.updates[0].zip }}", + "phone": "={{ $('Extract Updates').item.json.updates[0].phone }}", + "cell": "={{ $('Extract Updates').item.json.updates[0].cell }}", + "fax": "={{ $('Extract Updates').item.json.updates[0].fax }}", + "projects": "={{ $('Extract Updates').item.json.updates[0].projects }}", + "last": "={{ $('Extract Updates').item.json.updates[0].last }}", + "followUp": "={{ $('Extract Updates').item.json.updates[0].followUp }}" + }, + "matchingColumns": [], + "schema": [ + { + "id": "name", + "displayName": "name", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": true + }, + { + "id": "company", + "displayName": "company", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "address", + "displayName": "address", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "city", + "displayName": "city", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "state", + "displayName": "state", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "zip", + "displayName": "zip", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "phone", + "displayName": "phone", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "cell", + "displayName": "cell", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "fax", + "displayName": "fax", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "email", + "displayName": "email", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "projects", + "displayName": "projects", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "last", + "displayName": "last", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "followUp", + "displayName": "followUp", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + } + ], + "attemptToConvertTypes": false, + "convertFieldsToString": false + }, + "options": {} + }, + "type": "n8n-nodes-base.dataTable", + "typeVersion": 1, + "position": [ + 3072, + 160 + ], + "id": "39110f9d-d801-489b-b71e-1beb570659b6", + "name": "updateClientList", + "alwaysOutputData": true + }, + { + "parameters": { + "content": "to do:\nextract e-amil from the router header\nremove css/html from body for tocken prseervations" + }, + "type": "n8n-nodes-base.stickyNote", + "position": [ + 1184, + 640 + ], + "typeVersion": 1, + "id": "4511268c-de79-4a93-99d2-f484a0da7531", + "name": "Sticky Note" + }, + { + "parameters": { + "operation": "get", + "dataTableId": { + "__rl": true, + "value": "RE5GmewdBpOuPS4E", + "mode": "list", + "cachedResultName": "clientList", + "cachedResultUrl": "/projects/MAbDV0ZMuTHgonhb/datatables/RE5GmewdBpOuPS4E" + }, + "filters": { + "conditions": [ + { + "keyValue": "1" + } + ] + } + }, + "type": "n8n-nodes-base.dataTable", + "typeVersion": 1, + "position": [ + 784, + 0 + ], + "id": "e5fe4479-acf9-4762-a310-2fe33e50cfd8", + "name": "checkForClientList" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 2 + }, + "conditions": [ + { + "id": "98efa086-199d-4922-abfd-2df24514c2ee", + "leftValue": "={{ $json.id }}", + "rightValue": "1", + "operator": { + "type": "number", + "operation": "exists", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [ + 944, + 0 + ], + "id": "248f1064-6cfb-4a1b-8772-acb351b26b51", + "name": "doesClientListExist" + } + ], + "connections": { + "When clicking ‘Execute workflow’": { + "main": [ + [ + { + "node": "sessionIdCreate", + "type": "main", + "index": 0 + } + ] + ] + }, + "AI Agent1": { + "main": [ + [ + { + "node": "Extract Updates", + "type": "main", + "index": 0 + } + ] + ] + }, + "OpenAI Chat Model1": { + "ai_languageModel": [ + [ + { + "node": "AI Agent1", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "Simple Memory1": { + "ai_memory": [ + [ + { + "node": "AI Agent1", + "type": "ai_memory", + "index": 0 + } + ] + ] + }, + "formatClientList": { + "main": [ + [ + { + "node": "checkForClientList", + "type": "main", + "index": 0 + } + ] + ] + }, + "importClientList": { + "main": [ + [ + { + "node": "formatClientList", + "type": "main", + "index": 0 + } + ] + ] + }, + "sessionIdCreate": { + "main": [ + [ + { + "node": "importClientList", + "type": "main", + "index": 0 + } + ] + ] + }, + "createClientDataTable": { + "main": [ + [ + { + "node": "importEmailBinary", + "type": "main", + "index": 0 + } + ] + ] + }, + "importEmailBinary": { + "main": [ + [ + { + "node": "createEmailString", + "type": "main", + "index": 0 + } + ] + ] + }, + "createEmailString": { + "main": [ + [ + { + "node": "loopOverEmail", + "type": "main", + "index": 0 + } + ] + ] + }, + "loopOverEmail": { + "main": [ + [], + [ + { + "node": "createPrompt", + "type": "main", + "index": 0 + } + ] + ] + }, + "createPrompt": { + "main": [ + [ + { + "node": "AI Agent1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parse & Apply Client Updates": { + "main": [ + [ + { + "node": "updateClientList", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract Updates": { + "main": [ + [ + { + "node": "Get row(s)", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get row(s)": { + "main": [ + [ + { + "node": "Parse & Apply Client Updates", + "type": "main", + "index": 0 + } + ] + ] + }, + "updateClientList": { + "main": [ + [ + { + "node": "loopOverEmail", + "type": "main", + "index": 0 + } + ] + ] + }, + "checkForClientList": { + "main": [ + [ + { + "node": "doesClientListExist", + "type": "main", + "index": 0 + } + ] + ] + }, + "doesClientListExist": { + "main": [ + [ + { + "node": "importEmailBinary", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "createClientDataTable", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "settings": { + "executionOrder": "v1" + }, + "staticData": null, + "meta": null, + "pinData": {}, + "versionId": "5245ff56-5391-4c67-948b-df21811ac123", + "triggerCount": 0, + "shared": [ + { + "createdAt": "2025-11-01T05:48:54.248Z", + "updatedAt": "2025-11-01T05:48:54.248Z", + "role": "workflow:owner", + "workflowId": "sMcGBE1qq5fln6EM", + "projectId": "oI3LZpkceKxAFXfg" + } + ], + "tags": [ + { + "createdAt": "2025-11-01T05:48:51.009Z", + "updatedAt": "2025-11-01T05:48:51.009Z", + "id": "Lf8JGZly51m5AWJv", + "name": "TAG" + } + ] +} \ No newline at end of file