{"workflow":{"id":13828,"name":"Create manager tasks from Keephub form submissions with OpenAI GPT-4.1","views":20,"recentViews":0,"totalViews":20,"createdAt":"2026-03-03T11:06:05.828Z","description":"### Who is this for\n\nOrganizations using Keephub for form management and task tracking. Perfect for HR teams handling employee requests (leave, equipment, onboarding), retail managers reviewing store submissions, or any workflow where form responses need manager follow-up.\n\n### What it does\n\nTransforms form submissions into intelligent, contextual tasks for managers—automatically.\n\nWhen an employee submits a form, AI analyzes the data and generates a custom follow-up task with relevant fields, assigned directly to their supervisor.\n\nZero manual configuration per form.\n\n### 📋 How it works\n\n- Capture — Webhook receives new form submission from Keephub\n- Identify — Fetches submitter profile and resolves their manager (parent org node)\n- Analyze — AI reads form data + schema and designs contextual task\n- Validate — Checks AI output for required fields and malformed data\n- Create — Posts task to Keephub with proper targeting, dates, and permissions\n- Link — Includes URL back to original submission for manager reference\n\n### How to set up\n\n**📦 Prerequisites:**\n\n- n8n-nodes-keephub verified node (v1.5+) installed\n- Keephub account with API access (Login + Bearer tokens)\n- OpenAI API key (GPT-4.1 recommended)\n- Knowledge of Keephub orgchart relations and groups\n\n**Setup steps:**\n\n- Install n8n-nodes-keephub from Community Nodes in n8n settings\n- Create Keephub Login credential → connect to 4 blue nodes (Get Submitter, Get Parent Node, Get Root Node, Get Form Schema)\n- Create Keephub Bearer credential → connect to \"Create Task\" node\n- Create OpenAI credential → connect to \"Design Task with AI\" node\n- Open ⚙️ Config node → set groupId to target specific user groups (find in Keephub admin)\n- Activate workflow and copy webhook URL\n- Configure webhook in Keephub form settings\n- Test with a form submission and **n8n Webhook trigger**","workflow":{"meta":{"instanceId":"","templateCredsSetupCompleted":true},"nodes":[{"id":"5f046833-986d-4b88-8047-e6bec5e006b6","name":"Keephub Form Webhook","type":"n8n-nodes-base.webhook","position":[3696,3392],"webhookId":"cb6cf0c4-7a2b-42b9-a3da-a11af05ec1d5","parameters":{"path":"keephub-form-to-task","options":{},"httpMethod":"POST"},"typeVersion":2.1},{"id":"747fd501-63b4-4996-bd5d-d3ffb1778ea2","name":"📋 Overview","type":"n8n-nodes-base.stickyNote","position":[3344,2816],"parameters":{"width":540,"height":800,"content":"## 📋 Keephub Form → AI Task Creator\n\n**What it does:**\nAutomatically creates intelligent follow-up tasks for managers when employees submit forms. AI analyzes the submission and generates a contextual task with relevant fields—no manual work required.\n\n**How it works:**\n1. Employee submits any Keephub form\n2. Workflow identifies their manager (parent org node)\n3. AI analyzes submission data and designs an appropriate task\n4. Task is created and assigned to the manager\n\n**Quick Setup:**\n1. **Credentials:** Connect Keephub Login (4 nodes), Keephub Bearer (Create Task), and OpenAI\n2. **Config:** Set `groupId` to target specific user groups (e.g., only managers)\n3. **Test:** Submit a form and watch the magic happen\n\n**Pro tip:** Works with ANY Keephub form—no per-form configuration needed."},"typeVersion":1},{"id":"9863427c-44d8-44fc-9c3d-d383cbddec57","name":"🚦 Gate","type":"n8n-nodes-base.stickyNote","position":[3888,3136],"parameters":{"color":7,"width":440,"height":476,"content":"## 🚦 Webhook Filter\n\n**Purpose:** Ensures only NEW form submissions trigger task creation.\n\n**Filters out:**\n• Status changes (approvals/rejections)\n• Edits to existing submissions\n• Any other webhook noise\n\n**Result:** Clean, predictable workflow execution."},"typeVersion":1},{"id":"509db577-2764-4119-a3c0-477c9d413ef5","name":"🔍 Context Resolution","type":"n8n-nodes-base.stickyNote","position":[4320,3056],"parameters":{"color":7,"width":2264,"height":904,"content":"## 🔍 Data Collection & Target Resolution\n\n**1. Extract Form Data**\nNormalizes all field types (NodeSelector, Radio, Checkbox, Date, etc.) into a clean, indexed structure.\n\n**2. Get Submitter**\nFetches the employee's profile and organizational unit memberships.\n\n**3. Resolve Org Node**\nDetermines which org node the submission belongs to:\n• Uses NodeSelector field value (if present in form)\n• Falls back to submitter's primary orgunit\n• Flexible: supports both explicit and automatic assignment\n\n**4. Get Parent Node**\nFinds the manager one level up in the hierarchy (with error handling).\n\n**5. Get Root Node** (fallback)\nIf no parent exists (submitter is at/near top), fetches the actual organizational root to prevent targeting errors.\n\n**6. Get Form Schema**\nRetrieves field labels and structure for building human-readable AI prompts.\n\n**Result:** Clean context ready for AI analysis."},"typeVersion":1},{"id":"36b456a7-ab81-4e8c-806b-e326f9337b04","name":"🤖 AI Task Design","type":"n8n-nodes-base.stickyNote","position":[7056,3056],"parameters":{"color":5,"width":1184,"height":1136,"content":"## 🤖 AI-Powered Task Generation\n\n**1. Build AI Prompt**\nCreates a structured prompt with:\n• Form title and all submitted field values\n• Task design rules (language, field types, validation)\n• Instructions for contextual, actionable output\n\n💡 **Customization tip:** For single-form use, edit the prompt in \"Build AI Prompt\" node to specify exactly which fields you need.\n\n**2. Design Task with AI**\nGPT-4.1 analyzes the submission and generates:\n• Context-aware task title (references specifics from form)\n• Manager instructions paragraph\n• 3-6 actionable form fields (RadioButtons, TextArea, DatePicker, etc.)\n\n**3. Compose Task**\n• Validates AI output (checks for required fields, malformed data)\n• Generates cryptographically strong field IDs\n• URL-encodes link back to original submission\n• Builds full Keephub API payload with dates, targeting, and permissions\n\n**4. Create Task**\nPosts to Keephub API. Mission accomplished. ✅"},"typeVersion":1},{"id":"5bb25165-3a6e-458c-9363-07196a1fd88b","name":"⚙️ Config Note","type":"n8n-nodes-base.stickyNote","position":[6560,3056],"parameters":{"color":4,"width":500,"height":644,"content":"## ⚙️ Configuration\n\nEdit these values in the **⚙️ Config** node:\n\n**`groupId`** (string, recommended)\nKeephub group ID to target (e.g., \"65f8a3b2c1234567890abcde\").\n• **With groupId:** Task assigned only to users in that group\n• **Without groupId:** Task assigned to EVERYONE on the parent node\n• Find group IDs in Keephub admin panel\n\n**`taskDueDaysFromNow`** (number, default: 1)\nDays from now until task is due.\n• Must be ≥ 0 (validated)\n• Examples: 1 = tomorrow, 7 = next week\n\n**`workflow2WebhookUrl`** (string, optional)\nWebhook URL to chain a follow-up workflow.\n• Attached as hookurl to created task\n• Leave empty if not needed"},"typeVersion":1},{"id":"57e1bcea-03b3-412b-bb3f-978c3d967c8a","name":"Status Change?","type":"n8n-nodes-base.if","position":[3920,3392],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"cond-status","operator":{"type":"boolean","operation":"exists","singleValue":true},"leftValue":"={{ $json.body.data.statusChanged }}","rightValue":""}]}},"typeVersion":2.2},{"id":"a6a5dfb7-2433-4811-bad6-b7a200f8c5fb","name":"Is Edit?","type":"n8n-nodes-base.if","position":[4112,3472],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"cond-edited","operator":{"type":"boolean","operation":"true","singleValue":true},"leftValue":"={{ $json.body.data.isEdited }}","rightValue":""}]}},"typeVersion":2.2},{"id":"f237ae01-ee10-446a-8e58-282537441177","name":"Extract Form Data","type":"n8n-nodes-base.code","position":[4384,3632],"parameters":{"jsCode":"const raw  = $json;\nconst body = raw.body  ?? raw;\nconst data = body.data ?? body;\n\nif (!data || typeof data !== 'object') {\n  throw new Error('Unexpected webhook payload. Got: ' + JSON.stringify(raw).substring(0, 300));\n}\n\nconst meta = {\n  formSubmissionId: data._id        ?? null,\n  contentRef:       data.contentRef ?? null,\n  submittedById:    data.updatedBy  ?? null,\n  submittedAt:      data.updatedAt  ?? null,\n};\n\nfunction normalize(field) {\n  const el = field.element;\n  const v  = field.value;\n  let value;\n  if (v == null) {\n    value = null;\n  } else if (el === 'NodeSelector') {\n    value = typeof v === 'object' && !Array.isArray(v)\n      ? { id: v.value, label: v.label ?? null, descendants: v.descendants ?? [] }\n      : v;\n  } else if (['RadioButtons', 'CheckboxGroup', 'Dropdown'].includes(el)) {\n    value = Array.isArray(v) ? v.map(o => o.value || o.text?.nl || o.key) : v;\n  } else if (el === 'Stars' || el === 'Smileys') {\n    value = typeof v === 'number' ? v : parseInt(v, 10) || v;\n  } else {\n    value = v;\n  }\n  return { ffId: field.ffId ?? null, element: el ?? 'Unknown', value, raw: v };\n}\n\nconst fields = (data.values ?? []).map(normalize);\nconst byFfId = Object.fromEntries(fields.filter(f => f.ffId).map(f => [f.ffId, f]));\n\nreturn [{ json: { ...meta, fields, byFfId } }];\n"},"typeVersion":2},{"id":"35c784a5-9e78-4458-af10-60fa3ba40868","name":"Get Submitter","type":"n8n-nodes-keephub.keephub","position":[4624,3632],"parameters":{"userId":"={{ $json.submittedById }}","resource":"user","authentication":"loginCredentials"},"typeVersion":1},{"id":"2885478b-5607-467a-8626-4bb031c6d2f0","name":"Resolve Org Node","type":"n8n-nodes-base.code","position":[4864,3632],"parameters":{"jsCode":"const formData = $('Extract Form Data').first().json;\nconst userData = $input.first().json;\n\n// Strategy 1: first NodeSelector in form fields\nconst ns = formData.fields.find(f => f.element === 'NodeSelector');\nlet orgNodeId = null;\nlet resolvedFrom = null;\n\nif (ns && ns.raw) {\n  orgNodeId = typeof ns.raw === 'object' ? ns.raw.value : String(ns.raw);\n  resolvedFrom = 'NodeSelector';\n}\n\n// Strategy 2: submitter's first orgunit\nif (!orgNodeId && userData.orgunits && userData.orgunits.length > 0) {\n  const first = userData.orgunits[0];\n  orgNodeId = typeof first === 'string' ? first : (first._id || first.id || String(first));\n  resolvedFrom = 'userProfile';\n}\n\nif (!orgNodeId) {\n  throw new Error(\n    'No org node found. No NodeSelector in form and user ' +\n    (userData._id || 'unknown') + ' has no orgunits.'\n  );\n}\n\nreturn [{ json: { orgNodeId, resolvedFrom } }];\n"},"typeVersion":2},{"id":"e254d82c-1c2d-4f3f-9e25-c366d17c0cba","name":"Get Parent Node","type":"n8n-nodes-keephub.keephub","onError":"continueErrorOutput","position":[5104,3632],"parameters":{"nodeId":"={{ $json.orgNodeId }}","resource":"orgchart","operation":"getParent","authentication":"loginCredentials"},"typeVersion":1},{"id":"993a4a4f-3e92-4fea-bd99-f57528dc44c8","name":"Get Form Schema","type":"n8n-nodes-keephub.keephub","position":[6304,3552],"parameters":{"contentId":"={{ $('Extract Form Data').item.json.contentRef }}","operation":"getById","authentication":"loginCredentials"},"typeVersion":1},{"id":"2ee0543f-fb0c-4c0b-8f2d-6117340b9cae","name":"⚙️ Config","type":"n8n-nodes-base.set","position":[6768,3552],"parameters":{"options":{},"assignments":{"assignments":[{"id":"cfg-001","name":"groupId","type":"string","value":""},{"id":"cfg-002","name":"taskDueDaysFromNow","type":"number","value":1},{"id":"cfg-003","name":"workflow2WebhookUrl","type":"string","value":""}]}},"typeVersion":3.4},{"id":"e7335a47-6017-4b2d-a712-47c9b04db9f1","name":"Build AI Prompt","type":"n8n-nodes-base.code","position":[7216,3552],"parameters":{"jsCode":"const formData = $('Extract Form Data').first().json;\nconst schema   = $('Get Form Schema').first().json;\n\nconst schemaFields = schema.form?.fields || schema.template?.form?.fields || [];\n\n// Lookup by ffId for precise matching\nconst schemaByFfId = {};\nschemaFields.forEach(sf => {\n  const key = sf.ffId || sf.field_name;\n  if (key) schemaByFfId[key] = sf;\n});\n\n// Fallback: sequential index over non-decorator fields\nconst inputFields = schemaFields.filter(\n  sf => !['Paragraph', 'Header', 'Section'].includes(sf.element)\n);\n\nconst rawTitle  = schema.title || schema.template?.title || {};\nconst formTitle = Object.values(rawTitle)[0] || 'Form submission';\n\nconst fieldLines = formData.fields.map((field, i) => {\n  const sf    = (field.ffId && schemaByFfId[field.ffId]) || inputFields[i];\n  const label = sf?.label ? Object.values(sf.label)[0] : `Field ${i + 1}`;\n  const el    = field.element;\n  let val;\n  if (field.value == null) {\n    val = '(not filled)';\n  } else if (el === 'NodeSelector' && typeof field.value === 'object') {\n    val = field.value.label || field.value.id;\n  } else if (Array.isArray(field.value)) {\n    val = field.value.join(', ');\n  } else {\n    val = String(field.value);\n  }\n  return `- ${label} [${el}]: ${val}`;\n}).join('\\n');\n\nconst prompt = `You are designing a follow-up task in Keephub for a retail manager.\\n\\nA \\\"${formTitle}\\\" was just submitted:\\n${fieldLines}\\n\\nDesign ONE actionable task the manager must complete in response.\\n\\nLanguage rules:\\n- Detect the language from the data (most likely \\\"nl\\\" or \\\"en\\\")\\n- Use the detected BCP-47 code as key in every title/label object\\n- All your text must be in that language\\n\\nTask design rules:\\n1. Title must reference specifics from this submission (name, store, form type) — never generic\\n2. First field must be a Paragraph briefly explaining what the manager must do\\n3. Only add fields the manager needs to fill in — never repeat submitted data\\n4. Labels must be specific and actionable (not \\\"Notes\\\" but \\\"Reason for approval\\\")\\n5. Match field types to content: RadioButtons for yes/no, DatePicker for dates, TextArea for justifications\\n6. 3–6 fields maximum\\n\\nAvailable elements (exact strings):\\n- \\\"TextInput\\\" \\\"TextArea\\\" \\\"NumberInput\\\" \\\"DatePicker\\\"\\n- \\\"RadioButtons\\\" \\\"Checkboxes\\\" \\\"Dropdown\\\" (these require options array)\\n- \\\"Stars\\\" \\\"Smileys\\\" \\\"Paragraph\\\" (Paragraph = static text, content goes in label)\\n\\nReturn ONLY valid JSON, no markdown fences:\\n{\\n  \\\"title\\\": { \\\"<lang>\\\": \\\"...\\\" },\\n  \\\"introText\\\": { \\\"<lang>\\\": \\\"One sentence context for the manager.\\\" },\\n  \\\"fields\\\": [\\n    { \\\"element\\\": \\\"Paragraph\\\", \\\"label\\\": { \\\"<lang>\\\": \\\"...\\\" } },\\n    { \\\"element\\\": \\\"RadioButtons\\\", \\\"label\\\": { \\\"<lang>\\\": \\\"...\\\" }, \\\"required\\\": true, \\\"options\\\": [{ \\\"text\\\": { \\\"<lang>\\\": \\\"A\\\" } }, { \\\"text\\\": { \\\"<lang>\\\": \\\"B\\\" } }] }\\n  ]\\n}`;\n\nreturn [{ json: { prompt } }];\n"},"typeVersion":2},{"id":"3315bc2d-0dc6-4838-a9b0-b4cf1a5c1696","name":"Design Task with AI","type":"@n8n/n8n-nodes-langchain.openAi","position":[7408,3552],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4.1","cachedResultName":"GPT-4.1"},"options":{},"responses":{"values":[{"content":"={{ $json.prompt }}"}]},"builtInTools":{}},"typeVersion":2.1},{"id":"c7a8f42f-9eb3-4e43-89ef-0c76537c4615","name":"Compose Task","type":"n8n-nodes-base.code","position":[7696,3552],"parameters":{"jsCode":"const cfg      = $('⚙️ Config').first().json;\nconst formData = $('Extract Form Data').first().json;\n\n// --- Parse AI response ---\n// Long extraction chain handles different OpenAI node output formats across versions\n// (structured output, message.content array, legacy choices[], raw text)\nconst aiContent = $input.first().json;\nconst aiRaw =\n  aiContent?.output?.[0]?.content?.find(b => b.type === 'output_text')?.text ||\n  aiContent?.output?.[0]?.content?.[0]?.text ||\n  (Array.isArray(aiContent?.message?.content)\n    ? aiContent.message.content.find(b => b.type === 'text')?.text\n    : aiContent?.message?.content) ||\n  aiContent?.choices?.[0]?.message?.content ||\n  aiContent?.text ||\n  null;\n\nif (!aiRaw) throw new Error('AI returned no content: ' + JSON.stringify(aiContent).substring(0, 300));\n\nlet aiTask;\ntry {\n  aiTask = JSON.parse(aiRaw.replace(/```json|```/g, '').trim());\n} catch (e) {\n  throw new Error('AI returned invalid JSON: ' + String(aiRaw).substring(0, 300));\n}\nif (Array.isArray(aiTask)) {\n  aiTask = { title: { en: 'Review submission' }, introText: { en: 'Please review.' }, fields: aiTask };\n}\nif (!aiTask.title || typeof aiTask.title !== 'object') throw new Error('Missing title');\nif (!aiTask.introText || typeof aiTask.introText !== 'object') throw new Error('Missing introText');\nif (!Array.isArray(aiTask.fields) || !aiTask.fields.length) throw new Error('Missing fields');\n\n// --- Config validation ---\nconst OBJECT_ID = /^[a-f0-9]{24}$/i;\nconst dueDays    = cfg.taskDueDaysFromNow ?? 1;\nif (typeof dueDays !== 'number' || dueDays < 0) {\n  throw new Error(`taskDueDaysFromNow must be >= 0, got: ${dueDays}`);\n}\n\n// Validate groupId — invalid non-empty strings will broadcast org-wide\nlet groupId = null;\nif (cfg.groupId && typeof cfg.groupId === 'string' && cfg.groupId.trim()) {\n  if (OBJECT_ID.test(cfg.groupId)) {\n    groupId = cfg.groupId;\n  } else {\n    throw new Error(\n      `Invalid groupId \"${cfg.groupId}\" — must be a 24-char hex ObjectID. ` +\n      `Without a valid groupId, task targets EVERYONE on the node. ` +\n      `Check Keephub admin for the correct group ID.`\n    );\n  }\n}\n\nconst webhookUrl = cfg.workflow2WebhookUrl || '';\n\nconst contentRef       = formData.contentRef       || '';\nconst formSubmissionId = formData.formSubmissionId || '';\n\n// --- Org target (from parent/root path) ---\n// Merge both paths: try Extract Parent Target first, fall back to Extract Root Target\nlet target;\ntry {\n  target = $('Extract Parent Target').first().json;\n  if (!target || !target.targetNodeId) throw new Error('No targetNodeId in parent');\n} catch {\n  try {\n    target = $('Extract Root Target').first().json;\n    if (!target || !target.targetNodeId) throw new Error('No targetNodeId in root');\n  } catch (err2) {\n    throw new Error(\n      'No target node resolved from either parent or root path. ' +\n      'Check Get Parent Node / Get Root Node outputs.'\n    );\n  }\n}\nconst targetId          = target.targetNodeId;\nconst targetDescendants = target.descendants || [];\nconst resolvedGroups    = groupId ? [groupId] : [];\n\nif (!targetId) throw new Error('No target node resolved');\n\n// --- Dates (n8n global DateTime = Luxon) ---\nconst now       = DateTime.now();\nconst startDate = now.startOf('day').plus({ minutes: 1 });\nconst dueDate   = now.plus({ days: dueDays }).endOf('day');\nconst timezone  = now.zoneName;\n\n// --- Helpers ---\n// Generate cryptographically strong ObjectID-like strings\nfunction objectId() {\n  const buf = crypto.randomBytes(12);\n  return buf.toString('hex');\n}\n\nconst langs = Object.keys(aiTask.title);\nconst linkLabels = { nl: 'Bekijk de originele inzending', en: 'View the original submission' };\n\n// --- Intro paragraph with link to submission (URL-encoded) ---\nconst encodedContentRef = encodeURIComponent(contentRef);\nconst encodedSubmissionId = encodeURIComponent(formSubmissionId);\nconst introField = {\n  id: objectId(), required: false, element: 'Paragraph',\n  static: true, bold: false, italic: false, content: '',\n  text: Object.fromEntries(langs.map(lang => [\n    lang,\n    `<p>${aiTask.introText[lang] || ''} <a href=\\\"/system/content/${encodedContentRef}/formValueId/${encodedSubmissionId}/view\\\">${linkLabels[lang] || linkLabels.en}</a>.</p>`\n  ])),\n  type: '', editingFieldId: '', sectionIndex: null, insideSection: null, orderIndex: 0\n};\n\n// --- Build dynamic fields with validation ---\nconst OPTION_ELEMENTS = ['RadioButtons', 'Checkboxes', 'Dropdown'];\n\nconst dynamicFields = aiTask.fields\n  .filter(f => f.element && (f.label || f.element === 'Paragraph'))\n  .map((f, index) => {\n    const id  = objectId();\n    const isParagraph = f.element === 'Paragraph';\n    \n    // Validate Paragraph has label\n    if (isParagraph && (!f.label || typeof f.label !== 'object' || !Object.keys(f.label).length)) {\n      throw new Error(`AI returned Paragraph field at index ${index} with no label. All Paragraph fields must have a label object.`);\n    }\n    \n    const field = {\n      id, required: f.required ?? false, element: f.element,\n      type: '', editingFieldId: '', sectionIndex: null, insideSection: null,\n      orderIndex: index + 1\n    };\n    if (isParagraph) {\n      Object.assign(field, { static: true, bold: false, italic: false, content: '', text: f.label });\n    } else {\n      field.field_name = id;\n      field.label = f.label;\n    }\n    if (OPTION_ELEMENTS.includes(f.element)) {\n      // Validate options array\n      if (!Array.isArray(f.options) || !f.options.length) {\n        throw new Error(\n          `AI returned ${f.element} field at index ${index} with no options array. ` +\n          `${f.element} requires at least one option.`\n        );\n      }\n      // Validate each option has text object\n      f.options.forEach((opt, i) => {\n        if (!opt.text || typeof opt.text !== 'object' || !Object.keys(opt.text).length) {\n          throw new Error(\n            `AI returned ${f.element} field at index ${index}, option ${i} with malformed text. ` +\n            `Expected {\"<lang>\": \"...\"}, got: ${JSON.stringify(opt.text)}`\n          );\n        }\n      });\n      field.shouldTranslate = false;\n      field.hiddenValues = false;\n      field.options = f.options.map(opt => ({\n        value: '', text: opt.text, showRemark: false, remark: '', key: objectId()\n      }));\n      if (f.element !== 'Dropdown') field.hiddenRemarks = false;\n    }\n    if (f.element === 'TextArea') field.multiline = true;\n    if (f.element === 'NumberInput') { if (f.min != null) field.min = f.min; if (f.max != null) field.max = f.max; }\n    if (f.element === 'DatePicker') {\n      Object.assign(field, {\n        readOnly: false, defaultToday: false, dateFormat: 'MM/dd/yyyy',\n        timeFormat: 'hh:mm aa', showTimeSelect: false, showTimeSelectOnly: false\n      });\n    }\n    return field;\n  });\n\n// --- Final payload (single task, no repeat) ---\nreturn [{ json: {\n  sendPushNotification:  true,\n  orgchartSelection:     { include: [targetId], exclude: targetDescendants },\n  parentRef:             null,\n  orgchartAttrSelection: [],\n  groups:                resolvedGroups,\n  type:                  'single',\n  template: {\n    originLanguage:  langs[0],\n    startDate:       startDate.toISO(),\n    dueDate:         dueDate.toISO(),\n    completionType:  'group',\n    timezone,\n    endGroup:        'never',\n    title:           aiTask.title,\n    attachments:     Object.fromEntries(langs.map(l => [l, []])),\n    relatedTags:     [],\n    form:            { active: true, fields: [introField, ...dynamicFields] },\n    hookurls:        webhookUrl ? [{ url: webhookUrl, active: true }] : [],\n    highlighted:     false,\n    templateEndDate: null\n  }\n}}];\n"},"typeVersion":2},{"id":"1e90ce6e-bc35-4ce1-99e4-6ed2383ca0bc","name":"Create Task","type":"n8n-nodes-keephub.keephub","position":[7936,3552],"parameters":{"resource":"task","taskJsonBody":"={{ $node[\"Compose Task\"].json }}","defineTaskInput":"json"},"typeVersion":1},{"id":"86f33f87-089d-4681-beba-0e7330a0a875","name":"Get Root Node","type":"n8n-nodes-keephub.keephub","position":[5344,3696],"parameters":{"nodeId":"={{ $('Resolve Org Node').item.json.orgNodeId }}","resource":"orgchart","operation":"getAncestors","authentication":"loginCredentials","additionalFields":{}},"credentials":{},"typeVersion":1},{"id":"11ae0758-4053-424a-80cf-af2d725f2154","name":"⚠️ Group Targeting","type":"n8n-nodes-base.stickyNote","position":[6560,3696],"parameters":{"color":3,"width":500,"height":496,"content":"## ⚠️ Critical: Group Targeting\n\n**Without `groupId` → task assigned to EVERYONE**\nManagers, employees, interns, contractors—all users on the parent node will see the task.\n\n**To target specific groups (recommended):**\n1. Open Keephub admin panel\n2. Navigate to Groups section\n3. Copy the 24-character group ID (e.g., \"65f8a3b2c1234567890abcde\")\n4. Paste into `groupId` field in **⚙️ Config** node\n\n**Why manual configuration?**\nKeephub API returns groups as opaque IDs with no human-readable names. Automatic detection would be unreliable and could cause targeting errors.\n\n**Invalid `groupId` = workflow fails** (validates format to prevent silent broadcasting).\n"},"typeVersion":1},{"id":"1ce770a8-8ffb-40f2-8275-9d1753099a5b","name":"Extract Parent Target","type":"n8n-nodes-base.code","position":[5344,3552],"parameters":{"jsCode":"const result = $input.first().json;\nconst parent = result.parent || result;\nif (!parent._id) throw new Error('Parent node has no _id');\nreturn [{ json: { targetNodeId: parent._id, descendants: parent.descendants || [], isRoot: false } }];\n"},"typeVersion":2},{"id":"fb288916-ad61-4d6a-bba3-a957fb9026bf","name":"Extract Root Target","type":"n8n-nodes-base.code","position":[5584,3696],"parameters":{"jsCode":"// Get ancestors returns array from current node up to root\n// The last item is the actual organizational root\nconst response = $input.first().json;\nlet root;\nif (Array.isArray(response)) {\n  // If ancestors array is returned, take the last (topmost) ancestor as root\n  root = response.length > 0 ? response[response.length - 1] : null;\n} else if (response.ancestors && Array.isArray(response.ancestors)) {\n  root = response.ancestors.length > 0 ? response.ancestors[response.ancestors.length - 1] : null;\n} else {\n  root = response;\n}\nif (!root || (!root._id && !root.id)) {\n  throw new Error('No root node found in ancestors response');\n}\nreturn [{ json: { targetNodeId: root._id || root.id, descendants: root.descendants || [], isRoot: true } }];\n"},"typeVersion":2}],"pinData":{},"connections":{"Is Edit?":{"main":[[],[{"node":"Extract Form Data","type":"main","index":0}]]},"Compose Task":{"main":[[{"node":"Create Task","type":"main","index":0}]]},"Get Root Node":{"main":[[{"node":"Extract Root Target","type":"main","index":0}]]},"Get Submitter":{"main":[[{"node":"Resolve Org Node","type":"main","index":0}]]},"⚙️ Config":{"main":[[{"node":"Build AI Prompt","type":"main","index":0}]]},"Status Change?":{"main":[[],[{"node":"Is Edit?","type":"main","index":0}]]},"Build AI Prompt":{"main":[[{"node":"Design Task with AI","type":"main","index":0}]]},"Get Form Schema":{"main":[[{"node":"⚙️ Config","type":"main","index":0}]]},"Get Parent Node":{"main":[[{"node":"Extract Parent Target","type":"main","index":0}],[{"node":"Get Root Node","type":"main","index":0}]]},"Resolve Org Node":{"main":[[{"node":"Get Parent Node","type":"main","index":0}]]},"Extract Form Data":{"main":[[{"node":"Get Submitter","type":"main","index":0}]]},"Design Task with AI":{"main":[[{"node":"Compose Task","type":"main","index":0}]]},"Extract Root Target":{"main":[[{"node":"Get Form Schema","type":"main","index":0}]]},"Keephub Form Webhook":{"main":[[{"node":"Status Change?","type":"main","index":0}]]},"Extract Parent Target":{"main":[[{"node":"Get Form Schema","type":"main","index":0}]]}}},"lastUpdatedBy":1,"workflowInfo":{"nodeCount":22,"nodeTypes":{"n8n-nodes-base.if":{"count":2},"n8n-nodes-base.set":{"count":1},"n8n-nodes-base.code":{"count":6},"n8n-nodes-base.webhook":{"count":1},"n8n-nodes-base.stickyNote":{"count":6},"n8n-nodes-keephub.keephub":{"count":5},"@n8n/n8n-nodes-langchain.openAi":{"count":1}}},"status":"published","readyToDemo":null,"user":{"name":"Niksa Perovic","username":"niksa90","bio":"Obsessed with product, automation, and everything AI. Jack of all trades, master of getting things done.","verified":true,"links":[""],"avatar":"https://gravatar.com/avatar/8116b70e0134b4ff5de5d9e1d87bd1f3ae7df875fb146d4c9e301a454fdceb5a?r=pg&d=retro&size=200"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","codex":{"data":{"alias":["Router","Filter","Condition","Logic","Boolean","Branch"],"details":"The IF node can be used to implement binary conditional logic in your workflow. You can set up one-to-many conditions to evaluate each item of data being inputted into the node. That data will either evaluate to TRUE or FALSE and route out of the node accordingly.\n\nThis node has multiple types of conditions: Bool, String, Number, and Date & Time.","resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/create-a-toxic-language-detector-for-telegram/","icon":"🤬","label":"Create a toxic language detector for Telegram in 4 step"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/automation-for-maintainers-of-open-source-projects/","icon":"🏷️","label":"How to automatically manage contributions to open-source projects"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.if/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"transform\"]","defaults":{"name":"If","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"displayName":"If","typeVersion":2,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":38,"icon":"fa:pen","name":"n8n-nodes-base.set","codex":{"data":{"alias":["Set","JS","JSON","Filter","Transform","Map"],"resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/automatically-pulling-and-visualizing-data-with-n8n/","icon":"📈","label":"Automatically pulling and visualizing data with n8n"},{"url":"https://n8n.io/blog/database-monitoring-and-alerting-with-n8n/","icon":"📡","label":"Database Monitoring and Alerting with n8n"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/","icon":"📱","label":"Building an expense tracking app in 10 minutes"},{"url":"https://n8n.io/blog/the-ultimate-guide-to-automate-your-video-collaboration-with-whereby-mattermost-and-n8n/","icon":"📹","label":"The ultimate guide to automate your video collaboration with Whereby, Mattermost, and n8n"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/","icon":"🧰","label":"Learn to Build Powerful API Endpoints Using Webhooks"},{"url":"https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/","icon":"📈","label":"How a Membership Development Manager automates his work and investments"},{"url":"https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/","icon":"📈","label":"A low-code bitcoin ticker built with QuestDB and n8n.io"},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.set/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Data Transformation"]}}},"group":"[\"input\"]","defaults":{"name":"Edit Fields"},"iconData":{"icon":"pen","type":"icon"},"displayName":"Edit Fields (Set)","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":47,"icon":"file:webhook.svg","name":"n8n-nodes-base.webhook","codex":{"data":{"alias":["HTTP","API","Build","WH"],"resources":{"generic":[{"url":"https://n8n.io/blog/learn-how-to-automatically-cross-post-your-content-with-n8n/","icon":"✍️","label":"Learn how to automatically cross-post your content with n8n"},{"url":"https://n8n.io/blog/running-n8n-on-ships-an-interview-with-maranics/","icon":"🛳","label":"Running n8n on ships: An interview with Maranics"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/","icon":" 🪢","label":"What are APIs and how to use them with no code"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/how-a-digital-strategist-uses-n8n-for-online-marketing/","icon":"💻","label":"How a digital strategist uses n8n for online marketing"},{"url":"https://n8n.io/blog/the-ultimate-guide-to-automate-your-video-collaboration-with-whereby-mattermost-and-n8n/","icon":"📹","label":"The ultimate guide to automate your video collaboration with Whereby, Mattermost, and n8n"},{"url":"https://n8n.io/blog/how-to-automatically-give-kudos-to-contributors-with-github-slack-and-n8n/","icon":"👏","label":"How to automatically give kudos to contributors with GitHub, Slack, and n8n"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/creating-custom-incident-response-workflows-with-n8n/","label":"How to automate every step of an incident response workflow"},{"url":"https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/","icon":"🧰","label":"Learn to Build Powerful API Endpoints Using Webhooks"},{"url":"https://n8n.io/blog/learn-how-to-use-webhooks-with-mattermost-slash-commands/","icon":"🦄","label":"Learn how to use webhooks with Mattermost slash commands"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"trigger\"]","defaults":{"name":"Webhook"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCI+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTM1IDM3Yy0yLjIgMC00LTEuOC00LTRzMS44LTQgNC00IDQgMS44IDQgNC0xLjggNC00IDQiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMzUgNDNjLTMgMC01LjktMS40LTcuOC0zLjdsMy4xLTIuNWMxLjEgMS40IDIuOSAyLjMgNC43IDIuMyAzLjMgMCA2LTIuNyA2LTZzLTIuNy02LTYtNmMtMSAwLTIgLjMtMi45LjdsLTEuNyAxTDIzLjMgMTZsMy41LTEuOSA1LjMgOS40YzEtLjMgMi0uNSAzLS41IDUuNSAwIDEwIDQuNSAxMCAxMFM0MC41IDQzIDM1IDQzIi8+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTE0IDQzQzguNSA0MyA0IDM4LjUgNCAzM2MwLTQuNiAzLjEtOC41IDcuNS05LjdsMSAzLjlDOS45IDI3LjkgOCAzMC4zIDggMzNjMCAzLjMgMi43IDYgNiA2czYtMi43IDYtNnYtMmgxNXY0SDIzLjhjLS45IDQuNi01IDgtOS44IDgiLz48cGF0aCBmaWxsPSIjZTkxZTYzIiBkPSJNMTQgMzdjLTIuMiAwLTQtMS44LTQtNHMxLjgtNCA0LTQgNCAxLjggNCA0LTEuOCA0LTQgNCIvPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0yNSAxOWMtMi4yIDAtNC0xLjgtNC00czEuOC00IDQtNCA0IDEuOCA0IDQtMS44IDQtNCA0Ii8+PHBhdGggZmlsbD0iI2U5MWU2MyIgZD0ibTE1LjcgMzQtMy40LTIgNS45LTkuN2MtMi0xLjktMy4yLTQuNS0zLjItNy4zIDAtNS41IDQuNS0xMCAxMC0xMHMxMCA0LjUgMTAgMTBjMCAuOS0uMSAxLjctLjMgMi41bC0zLjktMWMuMS0uNS4yLTEgLjItMS41IDAtMy4zLTIuNy02LTYtNnMtNiAyLjctNiA2YzAgMi4xIDEuMSA0IDIuOSA1LjFsMS43IDF6Ii8+PC9zdmc+"},"displayName":"Webhook","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":565,"icon":"fa:sticky-note","name":"n8n-nodes-base.stickyNote","codex":{"data":{"alias":["Comments","Notes","Sticky"],"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"input\"]","defaults":{"name":"Sticky Note","color":"#FFD233"},"iconData":{"icon":"sticky-note","type":"icon"},"displayName":"Sticky Note","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":834,"icon":"file:code.svg","name":"n8n-nodes-base.code","codex":{"data":{"alias":["cpde","Javascript","JS","Python","Script","Custom Code","Function"],"details":"The Code node allows you to execute JavaScript in your workflow.","resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Code"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTcxXzQ0MSkiPgo8cGF0aCBkPSJNMTcwLjI4MyA0OEgxOTYuNUMyMDMuMTI3IDQ4IDIwOC41IDQyLjYyNzQgMjA4LjUgMzZWMTJDMjA4LjUgNS4zNzI1OCAyMDMuMTI3IDAgMTk2LjUgMEgxNzAuMjgzQzEyNi4xIDAgOTAuMjgzIDM1LjgxNzIgOTAuMjgzIDgwVjE3NkM5MC4yODMgMjA2LjkyOCA2NS4yMTA5IDIzMiAzNC4yODMgMjMySDIzQzE2LjM3MjYgMjMyIDExIDIzNy4zNzIgMTEgMjQ0VjI2OEMxMSAyNzQuNjI3IDE2LjM3MjQgMjgwIDIyLjk5OTYgMjgwTDM0LjI4MyAyODBDNjUuMjEwOSAyODAgOTAuMjgzIDMwNS4wNzIgOTAuMjgzIDMzNlY0NDBDOTAuMjgzIDQ3OS43NjQgMTIyLjUxOCA1MTIgMTYyLjI4MyA1MTJIMTk2LjVDMjAzLjEyNyA1MTIgMjA4LjUgNTA2LjYyNyAyMDguNSA1MDBWNDc2QzIwOC41IDQ2OS4zNzMgMjAzLjEyNyA0NjQgMTk2LjUgNDY0SDE2Mi4yODNDMTQ5LjAyOCA0NjQgMTM4LjI4MyA0NTMuMjU1IDEzOC4yODMgNDQwVjMzNkMxMzguMjgzIDMwOS4wMjIgMTI4LjAxMSAyODQuNDQzIDExMS4xNjQgMjY1Ljk2MUMxMDYuMTA5IDI2MC40MTYgMTA2LjEwOSAyNTEuNTg0IDExMS4xNjQgMjQ2LjAzOUMxMjguMDExIDIyNy41NTcgMTM4LjI4MyAyMDIuOTc4IDEzOC4yODMgMTc2VjgwQzEzOC4yODMgNjIuMzI2OSAxNTIuNjEgNDggMTcwLjI4MyA0OFoiIGZpbGw9IiNGRjk5MjIiLz4KPHBhdGggZD0iTTMwNSAzNkMzMDUgNDIuNjI3NCAzMTAuMzczIDQ4IDMxNyA0OEgzNDIuOTc5QzM2MC42NTIgNDggMzc0Ljk3OCA2Mi4zMjY5IDM3NC45NzggODBWMTc2QzM3NC45NzggMjAyLjk3OCAzODUuMjUxIDIyNy41NTcgNDAyLjA5OCAyNDYuMDM5QzQwNy4xNTMgMjUxLjU4NCA0MDcuMTUzIDI2MC40MTYgNDAyLjA5OCAyNjUuOTYxQzM4NS4yNTEgMjg0LjQ0MyAzNzQuOTc4IDMwOS4wMjIgMzc0Ljk3OCAzMzZWNDMyQzM3NC45NzggNDQ5LjY3MyAzNjAuNjUyIDQ2NCAzNDIuOTc5IDQ2NEgzMTdDMzEwLjM3MyA0NjQgMzA1IDQ2OS4zNzMgMzA1IDQ3NlY1MDBDMzA1IDUwNi42MjcgMzEwLjM3MyA1MTIgMzE3IDUxMkgzNDIuOTc5QzM4Ny4xNjEgNTEyIDQyMi45NzggNDc2LjE4MyA0MjIuOTc4IDQzMlYzMzZDNDIyLjk3OCAzMDUuMDcyIDQ0OC4wNTEgMjgwIDQ3OC45NzkgMjgwSDQ5MEM0OTYuNjI3IDI4MCA1MDIgMjc0LjYyOCA1MDIgMjY4VjI0NEM1MDIgMjM3LjM3MyA0OTYuNjI4IDIzMiA0OTAgMjMyTDQ3OC45NzkgMjMyQzQ0OC4wNTEgMjMyIDQyMi45NzggMjA2LjkyOCA0MjIuOTc4IDE3NlY4MEM0MjIuOTc4IDM1LjgxNzIgMzg3LjE2MSAwIDM0Mi45NzkgMEgzMTdDMzEwLjM3MyAwIDMwNSA1LjM3MjU4IDMwNSAxMlYzNloiIGZpbGw9IiNGRjk5MjIiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTcxXzQ0MSI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Code","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":1250,"icon":"file:openAi.svg","name":"@n8n/n8n-nodes-langchain.openAi","codex":{"data":{"alias":["LangChain","ChatGPT","Sora","DallE","whisper","audio","transcribe","tts","assistant"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.openai/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Agents","Miscellaneous","Root Nodes"]}}},"group":"[\"transform\"]","defaults":{"name":"OpenAI"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTM2Ljg2NzEgMTYuMzcxOEMzNy43NzQ2IDEzLjY0OCAzNy40NjIxIDEwLjY2NDIgMzYuMDEwOCA4LjE4NjYxQzMzLjgyODIgNC4zODY1MyAyOS40NDA3IDIuNDMxNDkgMjUuMTU1NiAzLjM1MTUxQzIzLjI0OTMgMS4yMDM5NiAyMC41MTA1IC0wLjAxNzMxNDggMTcuNjM5MiAwLjAwMDE4NTUzM0MxMy4yNTkxIC0wLjAwOTgxNDY4IDkuMzcyNzMgMi44MTAyNSA4LjAyNTIgNi45Nzc4M0M1LjIxMTM5IDcuNTU0MSAyLjc4MjU4IDkuMzE1MzggMS4zNjEzIDExLjgxMTdDLTAuODM3NDkzIDE1LjYwMTggLTAuMzM2MjMyIDIwLjM3OTQgMi42MDEzMyAyMy42Mjk0QzEuNjkzODEgMjYuMzUzMiAyLjAwNjMyIDI5LjMzNzEgMy40NTc2IDMxLjgxNDZDNS42NDAxNSAzNS42MTQ3IDEwLjAyNzcgMzcuNTY5NyAxNC4zMTI4IDM2LjY0OTdDMTYuMjE3OSAzOC43OTczIDE4Ljk1NzkgNDAuMDE4NSAyMS44MjkyIDM5Ljk5OThDMjYuMjExOCA0MC4wMTEgMzAuMDk5NCAzNy4xODg1IDMxLjQ0NjkgMzMuMDE3MUMzNC4yNjA4IDMyLjQ0MDkgMzYuNjg5NiAzMC42Nzk2IDM4LjExMDggMjguMTgzM0M0MC4zMDcxIDI0LjM5MzIgMzkuODA0NiAxOS42MTk0IDM2Ljg2ODMgMTYuMzY5M0wzNi44NjcxIDE2LjM3MThaTTIxLjgzMTcgMzcuMzg2QzIwLjA3OCAzNy4zODg1IDE4LjM3OTIgMzYuNzc0NyAxNy4wMzI5IDM1LjY1MDlDMTcuMDk0MSAzNS42MTg1IDE3LjIwMDQgMzUuNTU5NyAxNy4yNjkxIDM1LjUxNzJMMjUuMjM0MyAzMC45MTcxQzI1LjY0MTggMzAuNjg1OCAyNS44OTE4IDMwLjI1MjEgMjUuODg5MyAyOS43ODMzVjE4LjU1NDNMMjkuMjU1NiAyMC40OTgxQzI5LjI5MTkgMjAuNTE1NiAyOS4zMTU3IDIwLjU1MDYgMjkuMzIwNyAyMC41OTA2VjI5Ljg4OTZDMjkuMzE1NyAzNC4wMjQ3IDI1Ljk2NjggMzcuMzc3MiAyMS44MzE3IDM3LjM4NlpNNS43MjY0IDMwLjUwNzFDNC44NDc2MyAyOC45ODk2IDQuNTMxMzcgMjcuMjEwOCA0LjgzMjYzIDI1LjQ4NDVDNC44OTEzOCAyNS41MTk1IDQuOTk1MTMgMjUuNTgzMiA1LjA2ODg4IDI1LjYyNTdMMTMuMDM0MSAzMC4yMjU4QzEzLjQzNzggMzAuNDYyMSAxMy45Mzc4IDMwLjQ2MjEgMTQuMzQyOCAzMC4yMjU4TDI0LjA2NjggMjQuNjEwN1YyOC40OTgzQzI0LjA2OTMgMjguNTM4MyAyNC4wNTA1IDI4LjU3NyAyNC4wMTkzIDI4LjYwMkwxNS45Njc5IDMzLjI1MDlDMTIuMzgxNSAzNS4zMTU5IDcuODAxNDQgMzQuMDg4NCA1LjcyNzY1IDMwLjUwNzFINS43MjY0Wk0zLjYzMDEgMTMuMTIwNUM0LjUwNTEyIDExLjYwMDQgNS44ODY0IDEwLjQzNzkgNy41MzE0NCA5LjgzNDE1QzcuNTMxNDQgOS45MDI5IDcuNTI3NjkgMTAuMDI0MSA3LjUyNzY5IDEwLjEwOTJWMTkuMzEwNkM3LjUyNTE5IDE5Ljc3ODEgNy43NzUxOSAyMC4yMTE5IDguMTgxNDUgMjAuNDQzMUwxNy45MDU0IDI2LjA1N0wxNC41MzkxIDI4LjAwMDhDMTQuNTA1MyAyOC4wMjMzIDE0LjQ2MjggMjguMDI3IDE0LjQyNTMgMjguMDEwOEw2LjM3MjY2IDIzLjM1ODJDMi43OTM4MyAyMS4yODU2IDEuNTY2MzEgMTYuNzA2OCAzLjYyODg1IDEzLjEyMTdMMy42MzAxIDEzLjEyMDVaTTMxLjI4ODIgMTkuNTU2OUwyMS41NjQyIDEzLjk0MTdMMjQuOTMwNiAxMS45OTkyQzI0Ljk2NDMgMTEuOTc2NyAyNS4wMDY4IDExLjk3MjkgMjUuMDQ0MyAxMS45ODkyTDMzLjA5NyAxNi42MzhDMzYuNjgyMSAxOC43MDkzIDM3LjkxMDggMjMuMjk1NyAzNS44Mzk1IDI2Ljg4MDhDMzQuOTYzMyAyOC4zOTgzIDMzLjU4MzIgMjkuNTYwOCAzMS45Mzk1IDMwLjE2NThWMjAuNjg5NEMzMS45NDMyIDIwLjIyMTkgMzEuNjk0NSAxOS43ODk0IDMxLjI4OTQgMTkuNTU2OUgzMS4yODgyWk0zNC42MzgzIDE0LjUxNDJDMzQuNTc5NSAxNC40NzggMzQuNDc1OCAxNC40MTU1IDM0LjQwMiAxNC4zNzNMMjYuNDM2OCA5Ljc3Mjg5QzI2LjAzMzEgOS41MzY2NCAyNS41MzMxIDkuNTM2NjQgMjUuMTI4MSA5Ljc3Mjg5TDE1LjQwNDEgMTUuMzg4VjExLjUwMDRDMTUuNDAxNiAxMS40NjA0IDE1LjQyMDQgMTEuNDIxNyAxNS40NTE2IDExLjM5NjdMMjMuNTAzIDYuNzUxNThDMjcuMDg5NCA0LjY4Mjc5IDMxLjY3NDUgNS45MTQwNiAzMy43NDIgOS41MDE2NEMzNC42MTU4IDExLjAxNjcgMzQuOTMyIDEyLjc5MDUgMzQuNjM1OCAxNC41MTQySDM0LjYzODNaTTEzLjU3NDEgMjEuNDQzMUwxMC4yMDY1IDE5LjQ5OTRDMTAuMTcwMiAxOS40ODE5IDEwLjE0NjUgMTkuNDQ2OCAxMC4xNDE1IDE5LjQwNjhWMTAuMTA3OUMxMC4xNDQgNS45Njc4MSAxMy41MDI4IDIuNjEyNzQgMTcuNjQyOSAyLjYxNTI0QzE5LjM5NDIgMi42MTUyNCAyMS4wODkyIDMuMjMwMjUgMjIuNDM1NSA0LjM1MDI4QzIyLjM3NDMgNC4zODI3OCAyMi4yNjkzIDQuNDQxNTMgMjIuMTk5MiA0LjQ4NDAzTDE0LjIzNDEgOS4wODQxM0MxMy44MjY2IDkuMzE1MzggMTMuNTc2NiA5Ljc0Nzg5IDEzLjU3OTEgMTAuMjE2N0wxMy41NzQxIDIxLjQ0MDZWMjEuNDQzMVpNMTUuNDAyOSAxNy41MDA2TDE5LjczNDIgMTQuOTk5M0wyNC4wNjU1IDE3LjQ5OTNWMjIuNTAwN0wxOS43MzQyIDI1LjAwMDdMMTUuNDAyOSAyMi41MDA3VjE3LjUwMDZaIiBmaWxsPSJibGFjayIvPgo8L3N2Zz4K"},"displayName":"OpenAI","typeVersion":2,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]}],"categories":[{"id":35,"name":"Document Extraction"},{"id":49,"name":"AI Summarization"}],"image":[]}}