{"workflow":{"id":14814,"name":"Score WhatsApp PDF resumes with OpenAI GPT-4o-mini and Supabase","views":0,"recentViews":0,"totalViews":0,"createdAt":"2026-04-06T15:17:28.987Z","description":"## WhatsApp Resume Ranking Bot — AI-Powered Career Score via PDF Upload\n\nLet job seekers check their resume strength directly on WhatsApp — no app, no sign-up, no friction. Users send a keyword, answer 2 quick questions, upload their PDF resume, and receive a personalized career score, ATS feedback, and rejection analysis in under 60 seconds.\n\n### Who is this for?\n\nCareer coaches, job portals, HR-tech startups, or recruitment agencies who want to offer a self-serve resume evaluation tool directly inside WhatsApp — where their audience already is.\n\n---\n\n### What this workflow does\n\n1. **Listens for incoming WhatsApp messages** via the WhatsApp Business Cloud API\n2. **Manages multi-turn conversation state** in Supabase — tracks each user's progress through the flow (idle → name → role → resume → processing)\n3. **Guides the user step-by-step** to provide their name, target job role, and PDF resume\n4. **Downloads the resume PDF** from WhatsApp's media server using the Business API\n5. **Extracts resume text** for analysis\n6. **Runs a Scoring Engine** that calculates a career score (0–100) based on resume content and target role\n7. **Calls OpenAI** (GPT-4o-mini) in parallel to generate rejection reasons and actionable improvement tips\n8. **Merges both results** and formats a final WhatsApp message\n9. **Sends the personalized report** back to the user — score, weaknesses, and what to fix\n\n---\n\n### Prerequisites\n\n- A **WhatsApp Business Cloud API** account (Meta Developer App in Live mode)\n- A **Supabase** project with a `whatsapp_sessions` table\n- An **OpenAI** API key (GPT-4o-mini recommended)\n- A **self-hosted n8n instance** or n8n Cloud\n\n---\n\n### Supabase table setup\n\nCreate a table called `whatsapp_sessions` with these columns:\n\n| Column | Type | Notes |\n|---|---|---|\n| `phone` | text | Primary key / unique |\n| `state` | text | Conversation state (IDLE, WAITING_NAME, etc.) |\n| `name` | text | User's name |\n| `target_role` | text | Job role they're targeting |\n| `started_at` | timestamptz | Session start time |\n| `updated_at` | timestamptz | Last activity timestamp |\n\n---\n\n### Setup steps\n\n1. **Connect WhatsApp Business API** credentials in n8n (Meta App token + Phone Number ID)\n2. **Connect OpenAI** credentials in n8n\n3. **Update the Supabase URL and API key** inside the `Conversation State Manager` Code node\n4. **Replace `YOUR_PHONE_NUMBER_HERE`** in the Send nodes with your WhatsApp Phone Number ID\n5. **Set up Meta webhook** pointing to your n8n WhatsApp Trigger URL, subscribed to `messages`\n6. **Activate the workflow** — users can now send `CHECK MY RANK` to trigger the bot\n\n---\n\n### Conversation flow\n```\nUser: CHECK MY RANK\nBot:  Intro + \"Type YES to start\"\nUser: YES\nBot:  \"What's your full name?\"\nUser: Rahul Sharma\nBot:  \"Which job role are you targeting?\"\nUser: Data Analyst\nBot:  \"Upload your resume as a PDF\"\nUser: [uploads PDF]\nBot:  \"Analyzing... hang tight 🔍\"\nBot:  [sends career score + rejection reasons + tips]\n```\n\n---\n\n### Customization\n\n- Modify the **Scoring Engine** Code node to adjust how the score is calculated (weights for skills, experience, formatting, etc.)\n- Edit the **OpenAI prompt** to change the tone or depth of feedback\n- Add a **Supabase insert** after analysis to log all submissions for your own analytics\n- Extend the flow to offer a **paid detailed report** or **booking link** after the free score\n\n---\n\n### ⚠️ Important notes\n\n- The WhatsApp App must be in **Live mode** (not sandbox) to receive messages from non-whitelisted numbers — requires completing Meta Business Verification\n- Only **PDF resumes** are supported; DOCX files are rejected with a helpful prompt\n- Session state persists in Supabase, so users can resume mid-flow if they get disconnected\n- The bot handles concurrent users independently via phone number as the session key","workflow":{"meta":{"instanceId":"886a78126a9b6719341e2c5e4179c9ee71f1b4f551138fa25fe40bdf67aaa100"},"nodes":[{"id":"30659302-22f3-4470-bf2f-2d8c80ae16bd","name":"Main Sticky","type":"n8n-nodes-base.stickyNote","position":[-1872,64],"parameters":{"color":2,"width":500,"height":456,"content":"## WhatsApp Career Ranking Bot\nAnalyze resumes via WhatsApp to provide career scores, ATS feedback, and ranking insights.\n\n\n### How it works\n1. User triggers bot via keyword.\n\n2. Collects name, target role, and PDF resume.\n\n3. Scoring Engine calculates metrics.\n\n4. AI identifies rejection reasons.\n\n5. Returns personalized report to user."},"typeVersion":1},{"id":"d89d7440-ad39-4ec5-aff8-2b9ba2dd72da","name":"WhatsApp Trigger","type":"n8n-nodes-base.whatsAppTrigger","position":[-1328,256],"webhookId":"c9ac953f-d882-47b5-b6e9-8148e1ab0e2e","parameters":{"options":{},"updates":["messages"]},"typeVersion":1},{"id":"41605855-506a-4e80-a14c-be618903af45","name":"Conversation State Manager","type":"n8n-nodes-base.code","position":[-1104,256],"parameters":{"jsCode":"// ============================================\n// CONVERSATION STATE MANAGER — PRODUCTION\n// All fields always saved unconditionally\n// ============================================\n\nconst SUPABASE_URL = 'https://zfyyiewkkjyzxnyiabvk.supabase.co';\nconst SUPABASE_KEY = 'eyJ_YOUR_JWT_TOKEN_HERE';\n\nconst readHeaders = {\n  'apikey': SUPABASE_KEY,\n  'Authorization': `Bearer ${SUPABASE_KEY}`,\n  'Content-Type': 'application/json'\n};\n\nconst upsertHeaders = {\n  'apikey': SUPABASE_KEY,\n  'Authorization': `Bearer ${SUPABASE_KEY}`,\n  'Content-Type': 'application/json',\n  'Prefer': 'resolution=merge-duplicates,return=minimal'\n};\n\nconst message = $input.first().json;\n\nif (!message.messages || !message.messages[0]) {\n  return [{ json: { action: 'IGNORE' } }];\n}\n\nconst msg = message.messages[0];\n\nif (msg.type === 'status' || (!msg.text && !msg.document)) {\n  return [{ json: { action: 'IGNORE' } }];\n}\n\nconst from = msg.from;\nconst hasText = !!msg.text;\nconst hasDocument = !!msg.document;\nconst userText = hasText ? msg.text.body.trim() : '';\nconst upperText = userText.toUpperCase().replace(/[^A-Z0-9 ]/g, '').trim();\n\n// Extract WhatsApp profile name from contacts array\nlet profileName = '';\ntry {\n  if (message.contacts && message.contacts[0] && message.contacts[0].profile) {\n    profileName = message.contacts[0].profile.name || '';\n  }\n} catch(e) { /* no contact info available */ }\n\n// ── LOAD SESSION FROM SUPABASE ──\nlet session = { state: 'IDLE', name: '', target_role: '', started_at: new Date().toISOString() };\n\ntry {\n  const rows = await this.helpers.httpRequest({\n    method: 'GET',\n    url: `${SUPABASE_URL}/rest/v1/whatsapp_sessions?phone=eq.${encodeURIComponent(from)}&select=*`,\n    headers: readHeaders,\n    json: true\n  });\n  if (Array.isArray(rows) && rows.length > 0) {\n    session = rows[0];\n    session.name = session.name || profileName || '';\n    session.target_role = session.target_role || '';\n  }\n} catch(e) {\n  console.error('Supabase read error:', e.message);\n}\n\n// Always update name from WhatsApp profile if not yet saved\nif (!session.name && profileName) {\n  session.name = profileName;\n}\n\nlet action = 'IGNORE';\nlet replyText = '';\n\n// ── STATE MACHINE ──\nswitch (session.state) {\n\n  case 'IDLE':\n    if (\n      upperText === 'CHECK MY RANK' ||\n      upperText === 'CHECK' ||\n      upperText === 'CHECKMYRANK' ||\n      upperText === 'START' ||\n      upperText === 'RANK'\n    ) {\n      action = 'SEND_INTRO';\n      session.state = 'WAITING_YES';\n      session.started_at = new Date().toISOString();\n      replyText = `*Career Ranking Engine* 👋\\n\\nI'll analyze your resume and show you:\\n\\n📊 Your *career score* (0-100)\\n❌ *Why you're getting rejected*\\n✅ *What to fix* to get more interviews\\n\\nThis takes about 60 seconds.\\n\\n👉 *Type YES to start*`;\n    } else {\n      action = 'SEND_HELP';\n      session.started_at = new Date().toISOString();\n      replyText = `👋 Want to know where you stand in the job market?\\n\\n📲 Type *CHECK MY RANK* to get started!`;\n    }\n    break;\n\n  case 'WAITING_YES':\n    if (\n      upperText === 'YES' ||\n      upperText === 'Y' ||\n      upperText === 'YEAH' ||\n      upperText === 'YEP' ||\n      upperText === 'SURE' ||\n      upperText === 'OK' ||\n      upperText === 'OKAY'\n    ) {\n      action = 'ASK_NAME';\n      session.state = 'WAITING_NAME';\n      replyText = `Great! 🚀\\n\\n*Step 1 of 3*\\n\\nWhat's your *full name*?\\n\\n_Example: Rahul Sharma_`;\n    } else if (upperText === 'NO' || upperText === 'N' || upperText === 'CANCEL') {\n      action = 'SEND_REPLY';\n      session.state = 'IDLE';\n      replyText = `No worries! Whenever you're ready, type *CHECK MY RANK* 💪`;\n    } else if (\n      upperText === 'CHECK MY RANK' ||\n      upperText === 'CHECK' ||\n      upperText === 'START'\n    ) {\n      action = 'SEND_INTRO';\n      session.state = 'WAITING_YES';\n      session.started_at = new Date().toISOString();\n      replyText = `*Career Ranking Engine* 👋\\n\\nI'll analyze your resume and show you:\\n\\n📊 Your *career score* (0-100)\\n❌ *Why you're getting rejected*\\n✅ *What to fix* to get more interviews\\n\\nThis takes about 60 seconds.\\n\\n👉 *Type YES to start*`;\n    } else {\n      action = 'SEND_REPLY';\n      replyText = `Please type *YES* to start or *NO* to cancel.`;\n    }\n    break;\n\n  case 'WAITING_NAME':\n    if (hasText && userText.length >= 2 && userText.length <= 100) {\n      action = 'ASK_ROLE';\n      session.name = userText;\n      session.state = 'WAITING_ROLE';\n      replyText = `Nice to meet you, *${session.name}*! 👋\\n\\n*Step 2 of 3*\\n\\nWhich *job role* are you targeting?\\n\\n_Example: Data Analyst, Frontend Developer, Product Manager_`;\n    } else {\n      action = 'SEND_REPLY';\n      replyText = `Please enter your full name (e.g. \"Rahul Sharma\"):`;\n    }\n    break;\n\n  case 'WAITING_ROLE':\n    if (hasText && userText.length >= 2 && userText.length <= 150) {\n      action = 'ASK_RESUME';\n      session.target_role = userText;\n      session.state = 'WAITING_RESUME';\n      replyText = `Got it — *${session.target_role}* 🎯\\n\\n*Step 3 of 3*\\n\\nUpload your *resume as a PDF* 📎\\n\\n_(Tap 📎 and attach your PDF)_`;\n    } else {\n      action = 'SEND_REPLY';\n      replyText = `Please enter a valid job role (e.g. \"Data Analyst\", \"Software Engineer\"):`;\n    }\n    break;\n\n  case 'WAITING_RESUME':\n    if (hasDocument) {\n      if (msg.document.mime_type === 'application/pdf') {\n        action = 'PROCESS_RESUME';\n        session.state = 'PROCESSING';\n        replyText = `📄 Resume received!\\n\\n⏳ *Analyzing your profile for ${session.target_role}...*\\n\\nHang tight, this takes 15-30 seconds! 🔍`;\n      } else {\n        action = 'SEND_REPLY';\n        replyText = `⚠️ Please upload a *PDF file* only. DOC/DOCX not supported.`;\n      }\n    } else {\n      action = 'SEND_REPLY';\n      replyText = `Please tap 📎 and attach your resume as a *PDF file*.`;\n    }\n    break;\n\n  case 'PROCESSING':\n    action = 'SEND_REPLY';\n    replyText = `⏳ Still analyzing... please wait a moment!`;\n    break;\n\n  default:\n    session.state = 'IDLE';\n    session.started_at = new Date().toISOString();\n    action = 'SEND_HELP';\n    replyText = `Type *CHECK MY RANK* to get started!`;\n}\n\n// ── SAVE SESSION — always send ALL fields ──\ntry {\n  await this.helpers.httpRequest({\n    method: 'POST',\n    url: `${SUPABASE_URL}/rest/v1/whatsapp_sessions?on_conflict=phone`,\n    headers: upsertHeaders,\n    body: {\n      phone: from,\n      state: session.state,\n      name: session.name || '',\n      target_role: session.target_role || '',\n      started_at: session.started_at || new Date().toISOString(),\n      updated_at: new Date().toISOString()\n    },\n    json: true\n  });\n} catch(e) {\n  console.error('Supabase save error:', e.message);\n}\n\nreturn [{\n  json: {\n    action,\n    replyText,\n    from,\n    session,\n    documentId: hasDocument ? msg.document.id : null,\n    messageType: hasDocument ? 'document' : 'text'\n  }\n}];"},"typeVersion":2},{"id":"a0388610-aa7a-4006-9b5f-ed2ec01b3152","name":"Route Action","type":"n8n-nodes-base.switch","position":[-880,256],"parameters":{"rules":{"values":[{"outputKey":"Process Resume","conditions":{"options":{"version":2,"caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"route-process","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.action }}","rightValue":"PROCESS_RESUME"}]},"renameOutput":true},{"outputKey":"Send Reply","conditions":{"options":{"version":2,"caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"route-reply","operator":{"type":"string","operation":"notEquals"},"leftValue":"={{ $json.action }}","rightValue":"PROCESS_RESUME"},{"id":"route-not-ignore","operator":{"type":"string","operation":"notEquals"},"leftValue":"={{ $json.action }}","rightValue":"IGNORE"}]},"renameOutput":true}]},"options":{"fallbackOutput":"none"}},"typeVersion":3.2},{"id":"dc85e1fc-1209-4e3c-beb6-94a6b7eed92e","name":"Send Conversation Reply","type":"n8n-nodes-base.whatsApp","position":[-656,352],"webhookId":"4a4ad01d-9e0e-4730-abe8-a91a192becb9","parameters":{"textBody":"={{ $json.replyText }}","operation":"send","phoneNumberId":"YOUR_PHONE_NUMBER_HERE","additionalFields":{},"recipientPhoneNumber":"={{ $json.from }}"},"typeVersion":1},{"id":"43c95c4c-b314-480b-b3a9-3d0a9afc2a1a","name":"Send Processing Message","type":"n8n-nodes-base.whatsApp","position":[-656,160],"webhookId":"2d4d9e83-6554-4c9d-aec5-f5f561829cfe","parameters":{"textBody":"={{ $json.replyText }}","operation":"send","phoneNumberId":"YOUR_PHONE_NUMBER_HERE","additionalFields":{},"recipientPhoneNumber":"={{ $json.from }}"},"typeVersion":1},{"id":"101eea2c-b3da-4142-8aac-55298f7d81de","name":"Get Resume URL","type":"n8n-nodes-base.whatsApp","position":[-432,160],"webhookId":"b4147abb-0bc5-42fd-9f08-0e4d6625bdf2","parameters":{"resource":"media","operation":"mediaUrlGet","mediaGetId":"={{ $('Conversation State Manager').item.json.documentId }}"},"typeVersion":1},{"id":"cb5b5d8f-449b-48e3-a41e-c9f1ca9a9b69","name":"Download Resume","type":"n8n-nodes-base.httpRequest","position":[-208,160],"parameters":{"url":"={{ $json.url }}","options":{"response":{"response":{"responseFormat":"file"}}},"authentication":"predefinedCredentialType","nodeCredentialType":"whatsAppApi"},"typeVersion":4.2},{"id":"a66ac75a-6a17-47c2-b588-e863c12648b8","name":"Extract Resume Text","type":"n8n-nodes-base.extractFromFile","position":[64,160],"parameters":{"options":{},"operation":"pdf"},"typeVersion":1},{"id":"638a5b48-bc0c-4c47-a0f1-79726cb1061f","name":"Set Resume Data","type":"n8n-nodes-base.set","position":[288,160],"parameters":{"options":{},"assignments":{"assignments":[{"id":"set-resume-text","name":"resume_text","type":"string","value":"={{ $json.text }}"},{"id":"set-target-role","name":"target_role","type":"string","value":"={{ $('Conversation State Manager').item.json.session.target_role }}"},{"id":"set-user-from","name":"from","type":"string","value":"={{ $('Conversation State Manager').item.json.from }}"}]}},"typeVersion":3.4},{"id":"cff64e41-74ec-4491-93df-152b5bdd4de6","name":"Scoring Engine","type":"n8n-nodes-base.code","position":[624,160],"parameters":{"jsCode":"// ============================================\n// REAL SCORING ENGINE — 5 Dimensions = 100pts\n// Impact(25) + Relevance(25) + ATS(20) + Clarity(15) + Completeness(15)\n// NO caps. NO random. NO fake data.\n// ============================================\n\nconst SUPABASE_URL = 'https://zfyyiewkkjyzxnyiabvk.supabase.co';\nconst SUPABASE_KEY = 'eyJ_YOUR_JWT_TOKEN_HERE';\n\nconst readHeaders = {\n  'apikey': SUPABASE_KEY,\n  'Authorization': `Bearer ${SUPABASE_KEY}`,\n  'Content-Type': 'application/json'\n};\nconst upsertHeaders = { ...readHeaders, 'Prefer': 'resolution=merge-duplicates,return=minimal' };\n\nconst resumeText = $input.first().json.resume_text || '';\nconst targetRole = ($input.first().json.target_role || '').toLowerCase().trim();\nconst from = $input.first().json.from || '';\nconst text = resumeText.toLowerCase();\nconst words = text.split(/\\s+/).filter(w => w.length > 0);\nconst wordCount = words.length;\n\n// 1. IMPACT SCORE (0-25)\nlet impactScore = 0;\nconst impactVerbs = [\n  'led','built','managed','created','developed','launched','grew','increased',\n  'reduced','improved','achieved','implemented','delivered','designed','scaled',\n  'optimized','automated','generated','drove','spearheaded','founded','pioneered',\n  'established','transformed','streamlined','negotiated','secured','acquired',\n  'deployed','migrated','published','awarded','trained','mentored','directed',\n  'accelerated','boosted','cut','saved','shipped','refactored','architected'\n];\nconst uniqueVerbsUsed = [...new Set(impactVerbs.filter(v => text.includes(v)))];\nimpactScore += Math.min(uniqueVerbsUsed.length * 1.3, 12);\nconst quantPattern = /\\d+[%+xk]|\\$\\d+|\\d+\\s*(million|billion|thousand|k\\b|users|customers|clients|revenue|growth|reduction|increase|improvement|efficiency|saving|profit|leads|conversions|downloads)/gi;\nconst quantMatches = (resumeText.match(quantPattern) || []).length;\nimpactScore += Math.min(quantMatches * 2.5, 13);\nimpactScore = Math.min(Math.round(impactScore), 25);\n\n// 2. RELEVANCE SCORE (0-25)\nlet relevanceScore = 0;\nconst roleKeywordsMap = {\n  'data analyst': ['sql','python','excel','tableau','power bi','data','analytics','statistics','visualization','dashboard','reporting','pandas','numpy','etl','bigquery','snowflake','looker'],\n  'software engineer': ['javascript','python','java','react','node','api','git','aws','docker','agile','rest','sql','typescript','ci/cd','microservices','spring','kafka'],\n  'frontend developer': ['html','css','javascript','react','vue','angular','typescript','responsive','ui','ux','figma','tailwind','next.js','sass','webpack','vite','redux'],\n  'backend developer': ['python','java','node','api','database','sql','nosql','aws','docker','microservices','rest','graphql','redis','kafka','postgresql','mongodb','spring'],\n  'full stack developer': ['javascript','react','node','python','sql','api','git','docker','aws','typescript','mongodb','postgresql','graphql','rest'],\n  'product manager': ['product','roadmap','stakeholder','agile','scrum','user research','a/b testing','kpi','metrics','strategy','prioritization','jira','okr','gtm','sprint','analytics'],\n  'marketing manager': ['marketing','campaign','seo','social media','content','brand','analytics','roi','digital','engagement','conversion','google ads','email marketing','crm','hubspot'],\n  'ux designer': ['ux','ui','figma','sketch','wireframe','prototype','user research','usability','design thinking','interaction design','accessibility','user testing','adobe xd'],\n  'devops engineer': ['docker','kubernetes','ci/cd','aws','terraform','jenkins','ansible','linux','monitoring','pipeline','cloud','infrastructure','helm','prometheus','grafana'],\n  'machine learning engineer': ['python','tensorflow','pytorch','ml','deep learning','neural network','nlp','model','training','scikit-learn','feature engineering','mlops','llm','huggingface'],\n  'data scientist': ['python','r','machine learning','statistics','pandas','scikit-learn','tensorflow','sql','visualization','hypothesis','regression','classification','clustering','nlp'],\n  'business analyst': ['business analysis','requirements','stakeholder','sql','process','documentation','jira','agile','reporting','data','workflow','use case','gap analysis'],\n  'cloud engineer': ['aws','azure','gcp','terraform','kubernetes','docker','ci/cd','s3','ec2','lambda','cloudformation','iam','vpc'],\n  'android developer': ['android','kotlin','java','xml','jetpack','firebase','mvvm','retrofit','room','gradle','material design','coroutines'],\n  'ios developer': ['ios','swift','xcode','uikit','swiftui','firebase','alamofire','mvvm','core data','objective-c'],\n  'project manager': ['project management','pmp','agile','scrum','waterfall','budget','stakeholder','risk','timeline','deliverables','jira','ms project','gantt'],\n  'cybersecurity': ['security','penetration testing','firewall','siem','soc','vulnerability','encryption','compliance','iso27001','owasp','incident response','threat']\n};\n\nlet matchedKeywords = [];\nlet bestRoleMatch = null;\n\nfor (const [role, keywords] of Object.entries(roleKeywordsMap)) {\n  if (targetRole.includes(role) || role.includes(targetRole)) {\n    matchedKeywords = keywords; bestRoleMatch = role; break;\n  }\n}\nif (!bestRoleMatch) {\n  let bestOverlap = 0;\n  const targetWords = targetRole.split(' ');\n  for (const [role, keywords] of Object.entries(roleKeywordsMap)) {\n    const overlap = role.split(' ').filter(w => targetWords.some(tw => tw.includes(w) || w.includes(tw))).length;\n    if (overlap > bestOverlap) { bestOverlap = overlap; matchedKeywords = keywords; bestRoleMatch = role; }\n  }\n}\nif (matchedKeywords.length === 0) {\n  matchedKeywords = ['experience','team','project','management','communication','leadership','skills','results','strategy','analysis'];\n}\nconst keywordHits = matchedKeywords.filter(kw => text.includes(kw)).length;\nrelevanceScore = Math.round((keywordHits / matchedKeywords.length) * 25);\n\n// 3. ATS READINESS SCORE (0-20)\nlet atsScore = 0;\nconst sections = ['experience','education','skills','summary','objective','projects','certifications','achievements','work history','accomplishments','awards','publications'];\natsScore += Math.min(sections.filter(s => text.includes(s)).length * 2, 8);\nif (/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/.test(resumeText)) atsScore += 3;\nif (/\\+?\\d[\\d\\s\\-().]{7,}/.test(resumeText)) atsScore += 2;\nif (/linkedin\\.com\\/in\\//i.test(resumeText)) atsScore += 2;\nif (/github\\.com\\//i.test(resumeText)) atsScore += 2;\nconst specialDensity = (resumeText.match(/[^\\x00-\\x7F]/g) || []).length / Math.max(resumeText.length, 1);\nif (specialDensity < 0.005) atsScore += 3; else if (specialDensity < 0.02) atsScore += 1;\natsScore = Math.min(atsScore, 20);\n\n// 4. CLARITY SCORE (0-15)\nlet clarityScore = 0;\nconst sentences = resumeText.split(/[.!?]+/).filter(s => s.trim().length > 5);\nconst avgSentLen = sentences.length > 0 ? wordCount / sentences.length : 0;\nif (avgSentLen >= 8 && avgSentLen <= 20) clarityScore += 5; else if (avgSentLen > 0) clarityScore += 2;\nconst bulletCount = (resumeText.match(/[•\\-\\*]/g) || []).length;\nif (bulletCount >= 10) clarityScore += 5; else if (bulletCount >= 5) clarityScore += 3; else if (bulletCount >= 2) clarityScore += 1;\nconst yearRanges = (resumeText.match(/20\\d{2}\\s*[-to]+\\s*(20\\d{2}|present|current)/gi) || []).length;\nconst yearMentions = (resumeText.match(/20[0-2]\\d/g) || []).length;\nif (yearRanges >= 2) clarityScore += 5; else if (yearMentions >= 3) clarityScore += 3; else if (yearMentions >= 1) clarityScore += 1;\nclarityScore = Math.min(clarityScore, 15);\n\n// 5. COMPLETENESS SCORE (0-15)\nlet completenessScore = 0;\nif (wordCount >= 300 && wordCount <= 700) completenessScore += 6;\nelse if (wordCount >= 200 && wordCount <= 900) completenessScore += 4;\nelse if (wordCount >= 100) completenessScore += 2;\nif (/bachelor|master|phd|b\\.sc|m\\.sc|b\\.tech|m\\.tech|bca|mca|degree|university|college/i.test(resumeText)) completenessScore += 4;\nif (/certif|certified|aws certified|google cloud|azure|pmp|cpa|cfa|cissp|comptia|coursera|udemy/i.test(resumeText)) completenessScore += 3;\nif (/project/i.test(resumeText)) completenessScore += 2;\ncompletenessScore = Math.min(completenessScore, 15);\n\n// FINAL SCORE — true 0-100, no cap, no randomness\nconst finalScore = impactScore + relevanceScore + atsScore + clarityScore + completenessScore;\nconst normalizedRole = bestRoleMatch || targetRole;\n\n// SAVE SCORE TO SUPABASE\ntry {\n  await this.helpers.httpRequest({\n    method: 'POST',\n    url: `${SUPABASE_URL}/rest/v1/resume_scores?on_conflict=phone,target_role`,\n    headers: upsertHeaders,\n    body: { phone: from, target_role: normalizedRole, score: finalScore, scored_at: new Date().toISOString() },\n    json: true\n  });\n} catch(e) { console.error('Score save error:', e.message); }\n\n// FETCH REAL CANDIDATE STATS FROM SUPABASE\nlet totalCandidates = 1, rank = 1, percentile = 'First submission!', percentileLabel = '🌟';\ntry {\n  const allScores = await this.helpers.httpRequest({\n    method: 'GET',\n    url: `${SUPABASE_URL}/rest/v1/resume_scores?target_role=eq.${encodeURIComponent(normalizedRole)}&select=score&order=score.desc`,\n    headers: readHeaders,\n    json: true\n  });\n  if (Array.isArray(allScores) && allScores.length > 1) {\n    totalCandidates = allScores.length;\n    const sorted = allScores.map(r => r.score).sort((a, b) => b - a);\n    rank = sorted.findIndex(s => s <= finalScore) + 1;\n    if (rank <= 0) rank = totalCandidates;\n    const belowCount = allScores.filter(r => r.score < finalScore).length;\n    const pct = Math.round((belowCount / totalCandidates) * 100);\n    if (pct >= 85)      { percentile = 'Top 15%';    percentileLabel = '🟢'; }\n    else if (pct >= 70) { percentile = 'Top 30%';    percentileLabel = '🟡'; }\n    else if (pct >= 50) { percentile = 'Top 50%';    percentileLabel = '🟠'; }\n    else if (pct >= 35) { percentile = 'Top 65%';    percentileLabel = '🔴'; }\n    else                { percentile = 'Bottom 35%'; percentileLabel = '🔴'; }\n  }\n} catch(e) { console.error('Stats fetch error:', e.message); }\n\n// INTERVIEW PROBABILITY — honest, can reach High\nlet probability;\nif (finalScore >= 80)      probability = 'High';\nelse if (finalScore >= 65) probability = 'Moderate';\nelse if (finalScore >= 50) probability = 'Low';\nelse                       probability = 'Very Low';\n\n// EXTRACT EMAIL FROM RESUME\nlet extractedEmail = '';\nconst emailMatch = resumeText.match(/[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}/);\nif (emailMatch) extractedEmail = emailMatch[0];\n\n// EXTRACT LINKEDIN URL FROM RESUME\nlet extractedLinkedin = '';\nconst linkedinMatch = resumeText.match(/(?:https?:\\/\\/)?(?:www\\.)?linkedin\\.com\\/in\\/[a-zA-Z0-9_\\-]+\\/?/i);\nif (linkedinMatch) {\n  extractedLinkedin = linkedinMatch[0];\n  if (!extractedLinkedin.startsWith('http')) extractedLinkedin = 'https://' + extractedLinkedin;\n}\n\n// SAVE EMAIL, LINKEDIN, RESUME TEXT TO WHATSAPP_SESSIONS\ntry {\n  await this.helpers.httpRequest({\n    method: 'PATCH',\n    url: `${SUPABASE_URL}/rest/v1/whatsapp_sessions?phone=eq.${encodeURIComponent(from)}`,\n    headers: { ...readHeaders, 'Prefer': 'return=minimal' },\n    body: {\n      email: extractedEmail,\n      linkedin_url: extractedLinkedin,\n      resume_url: ''\n    },\n    json: true\n  });\n} catch(e) { console.error('Email/LinkedIn/Resume save error:', e.message); }\n\nreturn [{\n  json: {\n    finalScore, percentile, percentileLabel, rank, totalCandidates, probability,\n    impactScore, relevanceScore, atsScore, clarityScore, completenessScore,\n    bestRoleMatch: normalizedRole, wordCount, extractedEmail, extractedLinkedin,\n    targetRole: $input.first().json.target_role, from, resume_text: resumeText\n  }\n}];"},"typeVersion":2},{"id":"9d78444a-c2bc-4e3c-8375-cc69a4576c6a","name":"OpenAI - Rejection Analysis","type":"n8n-nodes-base.httpRequest","position":[512,256],"parameters":{"url":"YOUR_API_ENDPOINT_HERE","method":"POST","options":{},"jsonBody":"={{ {\n  \"model\": \"gpt-4o-mini\",\n  \"max_tokens\": 800,\n  \"response_format\": { \"type\": \"json_object\" },\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are an expert ATS resume reviewer. Return ONLY valid JSON, no extra text.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Analyze this resume for the role of \" + $json.target_role + \".\\n\\nResume:\\n\" + $json.resume_text.substring(0, 3000) + \"\\n\\nReturn ONLY this JSON:\\n{\\n  \\\"rejection_reasons\\\": [\\n    { \\\"reason\\\": \\\"specific reason\\\" },\\n    { \\\"reason\\\": \\\"second reason\\\" },\\n    { \\\"reason\\\": \\\"third reason\\\" }\\n  ],\\n  \\\"fixes\\\": [\\n    { \\\"fix\\\": \\\"specific fix\\\" },\\n    { \\\"fix\\\": \\\"second fix\\\" },\\n    { \\\"fix\\\": \\\"third fix\\\" }\\n  ],\\n  \\\"one_liner\\\": \\\"one sentence summary of biggest weakness\\\"\\n}\"\n    }\n  ]\n} }}","sendBody":true,"specifyBody":"json","authentication":"predefinedCredentialType","nodeCredentialType":"openAiApi"},"typeVersion":4.2},{"id":"1f4dda0f-9866-4f94-8a65-cfd27aa081e6","name":"Parse AI Response","type":"n8n-nodes-base.set","position":[736,256],"parameters":{"options":{},"assignments":{"assignments":[{"id":"parse-ai","name":"ai_analysis","type":"object","value":"={{ JSON.parse($json.choices[0].message.content) }}"}]}},"typeVersion":3.4},{"id":"6eab4bb5-1fbc-4e56-b146-55054ec3cca8","name":"Merge Score + AI","type":"n8n-nodes-base.merge","position":[928,176],"parameters":{"mode":"combine","options":{},"combineBy":"combineAll"},"typeVersion":3},{"id":"ce82879b-2bce-44c9-87d6-a66aca4b609e","name":"Format Result Message","type":"n8n-nodes-base.code","position":[1120,176],"parameters":{"jsCode":"// FORMAT FINAL MESSAGE — PRODUCTION\n// No mock/fallback results. No share footer. No name.\n\nconst SUPABASE_URL = 'https://zfyyiewkkjyzxnyiabvk.supabase.co';\nconst SUPABASE_KEY = 'eyJ_YOUR_JWT_TOKEN_HERE';\n\nconst item = $input.first().json;\n\nconst score = item.finalScore;\nconst percentile = item.percentile;\nconst percentileLabel = item.percentileLabel;\nconst rank = item.rank;\nconst total = item.totalCandidates;\nconst probability = item.probability;\nconst targetRole = item.targetRole;\nconst ai = item.ai_analysis;\n\n// Score bar\nconst filled = Math.round(score / 10);\nconst empty = 10 - filled;\nconst scoreBar = '█'.repeat(filled) + '░'.repeat(empty);\n\n// Rejection reasons — real AI output only, no fallback\nlet rejectionBlock = '';\nai.rejection_reasons.slice(0, 3).forEach((r, i) => {\n  rejectionBlock += `${i + 1}. ${r.reason}\\n`;\n});\n\n// Fixes — real AI output only, no fallback\nlet fixesBlock = '';\nai.fixes.slice(0, 3).forEach((f, i) => {\n  fixesBlock += `${i + 1}. ${f.fix}\\n`;\n});\n\nconst oneLiner = ai.one_liner;\n\nconst message = `*CAREER RANK REPORT*\n━━━━━━━━━━━━━━━━━━\n\n🎯 Role: *${targetRole}*\n\n📊 *Your Score: ${score}/100*\n${scoreBar}\n\n📈 Percentile: ${percentileLabel} *${percentile}*\n🏅 Rank: *#${rank}* out of ${total}+ candidates\n⚡ Interview Probability: *${probability}*\n\n━━━━━━━━━━━━━━━━━━\n❌ *WHY YOU'RE GETTING REJECTED:*\n\n${rejectionBlock}\n💡 _${oneLiner}_\n\n━━━━━━━━━━━━━━━━━━\n✅ *WHAT TO FIX:*\n\n${fixesBlock}\n━━━━━━━━━━━━━━━━━━\n🚀 *Want to improve your rank?*\n\nGet personalized job recommendations 👇\n🔗 https://meracareer.io`;\n\n// Reset session to IDLE — preserve name, email, linkedin, resume\ntry {\n  await this.helpers.httpRequest({\n    method: 'PATCH',\n    url: `${SUPABASE_URL}/rest/v1/whatsapp_sessions?phone=eq.${encodeURIComponent(item.from)}`,\n    headers: {\n      'apikey': SUPABASE_KEY,\n      'Authorization': `Bearer ${SUPABASE_KEY}`,\n      'Content-Type': 'application/json',\n      'Prefer': 'return=minimal'\n    },\n    body: {\n      state: 'IDLE',\n      updated_at: new Date().toISOString()\n    },\n    json: true\n  });\n} catch(e) {\n  console.error('Session reset error:', e.message);\n}\n\nreturn [{\n  json: {\n    from: item.from,\n    message: message\n  }\n}];"},"typeVersion":2},{"id":"f6681ffc-734e-42fc-8a3c-e74bbbd49f8c","name":"Send Final Result","type":"n8n-nodes-base.whatsApp","position":[1312,176],"webhookId":"afb512db-5efd-46b8-a342-ac4138b5c70d","parameters":{"textBody":"={{ $json.message }}","operation":"send","phoneNumberId":"YOUR_PHONE_NUMBER_HERE","additionalFields":{},"recipientPhoneNumber":"={{ $json.from }}"},"typeVersion":1},{"id":"f7585811-4a59-4840-aa9d-af2e81cda42a","name":"Upload Resume to Storage","type":"n8n-nodes-base.code","position":[64,336],"parameters":{"jsCode":"// Upload PDF resume to Supabase Storage and save URL to DB\nconst SUPABASE_URL = 'https://zfyyiewkkjyzxnyiabvk.supabase.co';\nconst SUPABASE_KEY = 'eyJ_YOUR_JWT_TOKEN_HERE';\n\n// Get phone from Conversation State Manager\nconst phone = $('Conversation State Manager').item.json.from;\nconst safePhone = phone.replace(/[^a-zA-Z0-9]/g, '_');\nconst fileName = `${safePhone}_${Date.now()}.pdf`;\n\nlet resumeUrl = '$input.first().json.url';\n\ntry {\n  // ── Find the binary field dynamically ──\n  // n8n may name the field 'data', 'file', or something else depending on the HTTP node config\n  const allBinary = items[0].binary || {};\n  const binaryKey = Object.keys(allBinary)[0]; // grab the first (and only) binary field\n\n  console.log('Available binary keys:', JSON.stringify(Object.keys(allBinary)));\n\n  if (!binaryKey) {\n    // No binary in items — try reading from $input\n    const inputItem = $input.first();\n    const inputBinary = inputItem.binary || {};\n    const inputKey = Object.keys(inputBinary)[0];\n    console.log('Input binary keys:', JSON.stringify(Object.keys(inputBinary)));\n\n    if (!inputKey) {\n      console.log('ERROR: No binary data found anywhere. Skipping upload.');\n      return [{ json: { resume_url: '', phone, error: 'no_binary' } }];\n    }\n  }\n\n  // Use n8n's native getBinaryDataBuffer — this is the CORRECT way to read binary in Code nodes\n  const buffer = await this.helpers.getBinaryDataBuffer(0, binaryKey || 'data');\n  console.log('Buffer size:', buffer.length, 'bytes');\n\n  if (!buffer || buffer.length === 0) {\n    console.log('ERROR: Buffer is empty. Skipping upload.');\n    return [{ json: { resume_url: '', phone, error: 'empty_buffer' } }];\n  }\n\n  // Upload raw PDF buffer to Supabase Storage\n  const uploadResponse = await this.helpers.httpRequest({\n    method: 'POST',\n    url: `${SUPABASE_URL}/storage/v1/object/resumes/${encodeURIComponent(fileName)}`,\n    headers: {\n      'apikey': SUPABASE_KEY,\n      'Authorization': `Bearer ${SUPABASE_KEY}`,\n      'Content-Type': 'application/pdf',\n      'x-upsert': 'true'\n    },\n    body: buffer\n  });\n\n  console.log('Upload response:', JSON.stringify(uploadResponse));\n\n  // Supabase Storage returns { Key: 'resumes/filename.pdf' } on success\n  if (uploadResponse && (uploadResponse.Key || uploadResponse.Id || uploadResponse.id)) {\n    resumeUrl = `${SUPABASE_URL}/storage/v1/object/public/resumes/${encodeURIComponent(fileName)}`;\n    console.log('Resume uploaded successfully:', resumeUrl);\n\n    // Save resume URL to whatsapp_sessions table\n    await this.helpers.httpRequest({\n      method: 'PATCH',\n      url: `${SUPABASE_URL}/rest/v1/whatsapp_sessions?phone=eq.${encodeURIComponent(phone)}`,\n      headers: {\n        'apikey': SUPABASE_KEY,\n        'Authorization': `Bearer ${SUPABASE_KEY}`,\n        'Content-Type': 'application/json',\n        'Prefer': 'return=minimal'\n      },\n      body: { resume_url: resumeUrl },\n      json: true\n    });\n    console.log('Resume URL saved to DB:', resumeUrl);\n  } else {\n    console.error('Upload may have failed. Response was:', JSON.stringify(uploadResponse));\n    // Still try to build the URL — Supabase sometimes returns 200 with empty body on upsert\n    resumeUrl = `${SUPABASE_URL}/storage/v1/object/public/resumes/${encodeURIComponent(fileName)}`;\n\n    await this.helpers.httpRequest({\n      method: 'PATCH',\n      url: `${SUPABASE_URL}/rest/v1/whatsapp_sessions?phone=eq.${encodeURIComponent(phone)}`,\n      headers: {\n        'apikey': SUPABASE_KEY,\n        'Authorization': `Bearer ${SUPABASE_KEY}`,\n        'Content-Type': 'application/json',\n        'Prefer': 'return=minimal'\n      },\n      body: { resume_url: resumeUrl },\n      json: true\n    });\n    console.log('Resume URL saved (optimistic):', resumeUrl);\n  }\n} catch(e) {\n  console.error('Upload error:', e.message, e.stack);\n}\n\nreturn [{ json: { resume_url: resumeUrl, phone } }];"},"typeVersion":2},{"id":"e5a7b49f-21ef-483f-853f-f92c0b402ce5","name":"Section 1","type":"n8n-nodes-base.stickyNote","position":[-1344,64],"parameters":{"color":7,"width":1276,"height":456,"content":"## 1. Trigger & State\nHandles WhatsApp webhook and manages user conversation progress in Supabase."},"typeVersion":1},{"id":"3b52f41f-325a-49aa-b26a-ad68bf7da034","name":"Section 2","type":"n8n-nodes-base.stickyNote","position":[0,64],"parameters":{"color":7,"width":1516,"height":456,"content":"## 2. Processing & Analysis\nDownloads resume, extracts text, performs scoring, and generates AI improvement suggestions."},"typeVersion":1}],"pinData":{},"connections":{"Route Action":{"main":[[{"node":"Send Processing Message","type":"main","index":0}],[{"node":"Send Conversation Reply","type":"main","index":0}]]},"Get Resume URL":{"main":[[{"node":"Download Resume","type":"main","index":0}]]},"Scoring Engine":{"main":[[{"node":"Merge Score + AI","type":"main","index":0}]]},"Download Resume":{"main":[[{"node":"Extract Resume Text","type":"main","index":0},{"node":"Upload Resume to Storage","type":"main","index":0}]]},"Set Resume Data":{"main":[[{"node":"Scoring Engine","type":"main","index":0},{"node":"OpenAI - Rejection Analysis","type":"main","index":0}]]},"Merge Score + AI":{"main":[[{"node":"Format Result Message","type":"main","index":0}]]},"WhatsApp Trigger":{"main":[[{"node":"Conversation State Manager","type":"main","index":0}]]},"Parse AI Response":{"main":[[{"node":"Merge Score + AI","type":"main","index":1}]]},"Extract Resume Text":{"main":[[{"node":"Set Resume Data","type":"main","index":0}]]},"Format Result Message":{"main":[[{"node":"Send Final Result","type":"main","index":0}]]},"Send Processing Message":{"main":[[{"node":"Get Resume URL","type":"main","index":0}]]},"Conversation State Manager":{"main":[[{"node":"Route Action","type":"main","index":0}]]},"OpenAI - Rejection Analysis":{"main":[[{"node":"Parse AI Response","type":"main","index":0}]]}}},"lastUpdatedBy":1,"workflowInfo":{"nodeCount":19,"nodeTypes":{"n8n-nodes-base.set":{"count":2},"n8n-nodes-base.code":{"count":4},"n8n-nodes-base.merge":{"count":1},"n8n-nodes-base.switch":{"count":1},"n8n-nodes-base.whatsApp":{"count":4},"n8n-nodes-base.stickyNote":{"count":3},"n8n-nodes-base.httpRequest":{"count":2},"n8n-nodes-base.extractFromFile":{"count":1},"n8n-nodes-base.whatsAppTrigger":{"count":1}}},"status":"published","readyToDemo":null,"user":{"name":"Panth1823","username":"panth1823","bio":"Curious builder automating various problems using n8n","verified":true,"links":["https://www.linkedin.com/in/panth1823/"],"avatar":"https://gravatar.com/avatar/a456b345ac9e437cca46f9b3b9127be2beaed3e25ba50c4ec7e5b3854499821e?r=pg&d=retro&size=200"},"nodes":[{"id":19,"icon":"file:httprequest.svg","name":"n8n-nodes-base.httpRequest","codex":{"data":{"alias":["API","Request","URL","Build","cURL"],"resources":{"generic":[{"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/automatically-pulling-and-visualizing-data-with-n8n/","icon":"📈","label":"Automatically pulling and visualizing data with n8n"},{"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/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/running-n8n-on-ships-an-interview-with-maranics/","icon":"🛳","label":"Running n8n on ships: An interview with Maranics"},{"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/world-poetry-day-workflow/","icon":"📜","label":"Celebrating World Poetry Day with a daily poem in Telegram"},{"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/automate-designs-with-bannerbear-and-n8n/","icon":"🎨","label":"Automate Designs with Bannerbear and n8n"},{"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/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/how-to-use-the-http-request-node-the-swiss-army-knife-for-workflow-automation/","icon":"🧰","label":"How to use the HTTP Request Node - The Swiss Army Knife for Workflow Automation"},{"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-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/automations-for-activists/","icon":"✨","label":"How Common Knowledge use workflow automation for activism"},{"url":"https://n8n.io/blog/creating-scheduled-text-affirmations-with-n8n/","icon":"🤟","label":"Creating scheduled text affirmations with n8n"},{"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.httprequest/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"output\"]","defaults":{"name":"HTTP Request","color":"#0004F5"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MCAyMEM0MCA4Ljk1MzE0IDMxLjA0NjkgMCAyMCAwQzguOTUzMTQgMCAwIDguOTUzMTQgMCAyMEMwIDMxLjA0NjkgOC45NTMxNCA0MCAyMCA0MEMzMS4wNDY5IDQwIDQwIDMxLjA0NjkgNDAgMjBaTTIwIDM2Ljk0NThDMTguODg1MiAzNi45NDU4IDE3LjEzNzggMzUuOTY3IDE1LjQ5OTggMzIuNjk4NUMxNC43OTY0IDMxLjI5MTggMTQuMTk2MSAyOS41NDMxIDEzLjc1MjYgMjcuNjg0N0gyNi4xODk4QzI1LjgwNDUgMjkuNTQwMyAyNS4yMDQ0IDMxLjI5MDEgMjQuNTAwMiAzMi42OTg1QzIyLjg2MjIgMzUuOTY3IDIxLjExNDggMzYuOTQ1OCAyMCAzNi45NDU4Wk0xMi45MDY0IDIwQzEyLjkwNjQgMjEuNjA5NyAxMy4wMDg3IDIzLjE2NCAxMy4yMDAzIDI0LjYzMDVIMjYuNzk5N0MyNi45OTEzIDIzLjE2NCAyNy4wOTM2IDIxLjYwOTcgMjcuMDkzNiAyMEMyNy4wOTM2IDE4LjM5MDMgMjYuOTkxMyAxNi44MzYgMjYuNzk5NyAxNS4zNjk1SDEzLjIwMDNDMTMuMDA4NyAxNi44MzYgMTIuOTA2NCAxOC4zOTAzIDEyLjkwNjQgMjBaTTIwIDMuMDU0MTlDMjEuMTE0OSAzLjA1NDE5IDIyLjg2MjIgNC4wMzA3OCAyNC41MDAxIDcuMzAwMzlDMjUuMjA2NiA4LjcxNDA4IDI1LjgwNzIgMTAuNDA2NyAyNi4xOTIgMTIuMzE1M0gxMy43NTAxQzE0LjE5MzMgMTAuNDA0NyAxNC43OTQyIDguNzEyNTQgMTUuNDk5OCA3LjMwMDY0QzE3LjEzNzcgNC4wMzA4MyAxOC44ODUxIDMuMDU0MTkgMjAgMy4wNTQxOVpNMzAuMTQ3OCAyMEMzMC4xNDc4IDE4LjQwOTkgMzAuMDU0MyAxNi44NjE3IDI5LjgyMjcgMTUuMzY5NUgzNi4zMDQyQzM2LjcyNTIgMTYuODQyIDM2Ljk0NTggMTguMzk2NCAzNi45NDU4IDIwQzM2Ljk0NTggMjEuNjAzNiAzNi43MjUyIDIzLjE1OCAzNi4zMDQyIDI0LjYzMDVIMjkuODIyN0MzMC4wNTQzIDIzLjEzODMgMzAuMTQ3OCAyMS41OTAxIDMwLjE0NzggMjBaTTI2LjI3NjcgNC4yNTUxMkMyNy42MzY1IDYuMzYwMTkgMjguNzExIDkuMTMyIDI5LjM3NzQgMTIuMzE1M0gzNS4xMDQ2QzMzLjI1MTEgOC42NjggMzAuMTA3IDUuNzgzNDYgMjYuMjc2NyA0LjI1NTEyWk0xMC42MjI2IDEyLjMxNTNINC44OTI5M0M2Ljc1MTQ3IDguNjY3ODQgOS44OTM1MSA1Ljc4MzQxIDEzLjcyMzIgNC4yNTUxM0MxMi4zNjM1IDYuMzYwMjEgMTEuMjg5IDkuMTMyMDEgMTAuNjIyNiAxMi4zMTUzWk0zLjA1NDE5IDIwQzMuMDU0MTkgMjEuNjAzIDMuMjc3NDMgMjMuMTU3NSAzLjY5NDg0IDI0LjYzMDVIMTAuMTIxN0M5Ljk0NjE5IDIzLjE0MiA5Ljg1MjIyIDIxLjU5NDMgOS44NTIyMiAyMEM5Ljg1MjIyIDE4LjQwNTcgOS45NDYxOSAxNi44NTggMTAuMTIxNyAxNS4zNjk1SDMuNjk0ODRDMy4yNzc0MyAxNi44NDI1IDMuMDU0MTkgMTguMzk3IDMuMDU0MTkgMjBaTTI2LjI3NjYgMzUuNzQyN0MyNy42MzY1IDMzLjYzOTMgMjguNzExIDMwLjg2OCAyOS4zNzc0IDI3LjY4NDdIMzUuMTA0NkMzMy4yNTEgMzEuMzMyMiAzMC4xMDY4IDM0LjIxNzkgMjYuMjc2NiAzNS43NDI3Wk0xMy43MjM0IDM1Ljc0MjdDOS44OTM2OSAzNC4yMTc5IDYuNzUxNTUgMzEuMzMyNCA0Ljg5MjkzIDI3LjY4NDdIMTAuNjIyNkMxMS4yODkgMzAuODY4IDEyLjM2MzUgMzMuNjM5MyAxMy43MjM0IDM1Ljc0MjdaIiBmaWxsPSIjM0E0MkU5Ii8+Cjwvc3ZnPgo="},"displayName":"HTTP Request","typeVersion":4,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":24,"icon":"file:merge.svg","name":"n8n-nodes-base.merge","codex":{"data":{"alias":["Join","Concatenate","Wait"],"resources":{"generic":[{"url":"https://n8n.io/blog/how-to-sync-data-between-two-systems/","icon":"🏬","label":"How to synchronize data between two systems (one-way vs. two-way sync"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/migrating-community-metrics-to-orbit-using-n8n/","icon":"📈","label":"Migrating Community Metrics to Orbit using n8n"},{"url":"https://n8n.io/blog/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/","icon":"👦","label":"Build your own virtual assistant with n8n: A step by step guide"},{"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/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.merge/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Merge"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTc3XzUxOCkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgNDhDMCAyMS40OTAzIDIxLjQ5MDMgMCA0OCAwSDExMkMxMzguNTEgMCAxNjAgMjEuNDkwMyAxNjAgNDhWNTZIMTk2LjI1MkMyNDAuNDM1IDU2IDI3Ni4yNTIgOTEuODE3MiAyNzYuMjUyIDEzNlYxOTJDMjc2LjI1MiAyMTQuMDkxIDI5NC4xNjEgMjMyIDMxNi4yNTIgMjMySDM1MlYyMjRDMzUyIDE5Ny40OSAzNzMuNDkgMTc2IDQwMCAxNzZINDY0QzQ5MC41MSAxNzYgNTEyIDE5Ny40OSA1MTIgMjI0VjI4OEM1MTIgMzE0LjUxIDQ5MC41MSAzMzYgNDY0IDMzNkg0MDBDMzczLjQ5IDMzNiAzNTIgMzE0LjUxIDM1MiAyODhWMjgwSDMxNi4yNTJDMjk0LjE2MSAyODAgMjc2LjI1MiAyOTcuOTA5IDI3Ni4yNTIgMzIwVjM3NkMyNzYuMjUyIDQyMC4xODMgMjQwLjQzNSA0NTYgMTk2LjI1MiA0NTZIMTYwVjQ2NEMxNjAgNDkwLjUxIDEzOC41MSA1MTIgMTEyIDUxMkg0OEMyMS40OTAzIDUxMiAwIDQ5MC41MSAwIDQ2NFY0MDBDMCAzNzMuNDkgMjEuNDkwMyAzNTIgNDggMzUySDExMkMxMzguNTEgMzUyIDE2MCAzNzMuNDkgMTYwIDQwMFY0MDhIMTk2LjI1MkMyMTMuOTI1IDQwOCAyMjguMjUyIDM5My42NzMgMjI4LjI1MiAzNzZWMzIwQzIyOC4yNTIgMjk0Ljc4NCAyMzguODU5IDI3Mi4wNDQgMjU1Ljg1MyAyNTZDMjM4Ljg1OSAyMzkuOTU2IDIyOC4yNTIgMjE3LjIxNiAyMjguMjUyIDE5MlYxMzZDMjI4LjI1MiAxMTguMzI3IDIxMy45MjUgMTA0IDE5Ni4yNTIgMTA0SDE2MFYxMTJDMTYwIDEzOC41MSAxMzguNTEgMTYwIDExMiAxNjBINDhDMjEuNDkwMyAxNjAgMCAxMzguNTEgMCAxMTJWNDhaTTEwNCA0OEMxMDguNDE4IDQ4IDExMiA1MS41ODE3IDExMiA1NlYxMDRDMTEyIDEwOC40MTggMTA4LjQxOCAxMTIgMTA0IDExMkg1NkM1MS41ODE3IDExMiA0OCAxMDguNDE4IDQ4IDEwNFY1NkM0OCA1MS41ODE3IDUxLjU4MTcgNDggNTYgNDhIMTA0Wk00NTYgMjI0QzQ2MC40MTggMjI0IDQ2NCAyMjcuNTgyIDQ2NCAyMzJWMjgwQzQ2NCAyODQuNDE4IDQ2MC40MTggMjg4IDQ1NiAyODhINDA4QzQwMy41ODIgMjg4IDQwMCAyODQuNDE4IDQwMCAyODBWMjMyQzQwMCAyMjcuNTgyIDQwMy41ODIgMjI0IDQwOCAyMjRINDU2Wk0xMTIgNDA4QzExMiA0MDMuNTgyIDEwOC40MTggNDAwIDEwNCA0MDBINTZDNTEuNTgxNyA0MDAgNDggNDAzLjU4MiA0OCA0MDhWNDU2QzQ4IDQ2MC40MTggNTEuNTgxNyA0NjQgNTYgNDY0SDEwNEMxMDguNDE4IDQ2NCAxMTIgNDYwLjQxOCAxMTIgNDU2VjQwOFoiIGZpbGw9IiM1NEI4QzkiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTc3XzUxOCI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Merge","typeVersion":3,"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":112,"icon":"fa:map-signs","name":"n8n-nodes-base.switch","codex":{"data":{"alias":["Router","If","Path","Filter","Condition","Logic","Branch","Case"],"resources":{"generic":[{"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/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/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/","icon":"👦","label":"Build your own virtual assistant with n8n: A step by step guide"},{"url":"https://n8n.io/blog/automation-for-maintainers-of-open-source-projects/","icon":"🏷️","label":"How to automatically manage contributions to open-source projects"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.switch/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"transform\"]","defaults":{"name":"Switch","color":"#506000"},"iconData":{"icon":"map-signs","type":"icon"},"displayName":"Switch","typeVersion":3,"nodeCategories":[{"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":827,"icon":"file:whatsapp.svg","name":"n8n-nodes-base.whatsApp","codex":{"data":{"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.whatsapp/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/whatsapp/"}]},"categories":["Communication","HITL"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"HITL":["Human in the Loop"]}}},"group":"[\"output\"]","defaults":{"name":"WhatsApp Business Cloud"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0ibTQuODY4IDQzLjMwMyAyLjY5NC05LjgzNWExOC45NCAxOC45NCAwIDAgMS0yLjUzNS05LjQ4OUM1LjAzMiAxMy41MTQgMTMuNTQ4IDUgMjQuMDE0IDVhMTguODcgMTguODcgMCAwIDEgMTMuNDMgNS41NjZBMTguODcgMTguODcgMCAwIDEgNDMgMjMuOTk0Yy0uMDA0IDEwLjQ2NS04LjUyMiAxOC45OC0xOC45ODYgMTguOThoLS4wMDhhMTkgMTkgMCAwIDEtOS4wNzMtMi4zMTF6Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTQuODY4IDQzLjgwM2EuNS41IDAgMCAxLS40ODItLjYzMWwyLjYzOS05LjYzNmExOS41IDE5LjUgMCAwIDEtMi40OTctOS41NTZDNC41MzIgMTMuMjM4IDEzLjI3MyA0LjUgMjQuMDE0IDQuNWExOS4zNyAxOS4zNyAwIDAgMSAxMy43ODQgNS43MTNBMTkuMzYgMTkuMzYgMCAwIDEgNDMuNSAyMy45OTRjLS4wMDQgMTAuNzQxLTguNzQ2IDE5LjQ4LTE5LjQ4NiAxOS40OGExOS41NCAxOS41NCAwIDAgMS05LjE0NC0yLjI3N2wtOS44NzUgMi41ODlhLjUuNSAwIDAgMS0uMTI3LjAxNyIvPjxwYXRoIGZpbGw9IiNjZmQ4ZGMiIGQ9Ik0yNC4wMTQgNWExOC44NyAxOC44NyAwIDAgMSAxMy40MyA1LjU2NkExOC44NyAxOC44NyAwIDAgMSA0MyAyMy45OTRjLS4wMDQgMTAuNDY1LTguNTIyIDE4Ljk4LTE4Ljk4NiAxOC45OGgtLjAwOGExOSAxOSAwIDAgMS05LjA3My0yLjMxMWwtMTAuMDY1IDIuNjQgMi42OTQtOS44MzVhMTguOTQgMTguOTQgMCAwIDEtMi41MzUtOS40ODlDNS4wMzIgMTMuNTE0IDEzLjU0OCA1IDI0LjAxNCA1bTAtMUMxMi45OTggNCA0LjAzMiAxMi45NjIgNC4wMjcgMjMuOTc5YTIwIDIwIDAgMCAwIDIuNDYxIDkuNjIyTDMuOTAzIDQzLjA0YS45OTguOTk4IDAgMCAwIDEuMjE5IDEuMjMxbDkuNjg3LTIuNTRhMjAgMjAgMCAwIDAgOS4xOTcgMi4yNDRjMTEuMDI0IDAgMTkuOTktOC45NjMgMTkuOTk1LTE5Ljk4QTE5Ljg2IDE5Ljg2IDAgMCAwIDM4LjE1MyA5Ljg2IDE5Ljg3IDE5Ljg3IDAgMCAwIDI0LjAxNCA0Ii8+PHBhdGggZmlsbD0iIzQwYzM1MSIgZD0iTTM1LjE3NiAxMi44MzJhMTUuNjcgMTUuNjcgMCAwIDAtMTEuMTU3LTQuNjI2Yy04LjcwNCAwLTE1Ljc4MyA3LjA3Ni0xNS43ODcgMTUuNzc0YTE1Ljc0IDE1Ljc0IDAgMCAwIDIuNDEzIDguMzk2bC4zNzYuNTk3LTEuNTk1IDUuODIxIDUuOTczLTEuNTY2LjU3Ny4zNDJhMTUuNzUgMTUuNzUgMCAwIDAgOC4wMzIgMi4xOTloLjAwNmM4LjY5OCAwIDE1Ljc3Ny03LjA3NyAxNS43OC0xNS43NzZhMTUuNjggMTUuNjggMCAwIDAtNC42MTgtMTEuMTYxIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTE5LjI2OCAxNi4wNDVjLS4zNTUtLjc5LS43MjktLjgwNi0xLjA2OC0uODItLjI3Ny0uMDEyLS41OTMtLjAxMS0uOTA5LS4wMTFzLS44My4xMTktMS4yNjUuNTk0LTEuNjYxIDEuNjIyLTEuNjYxIDMuOTU2IDEuNyA0LjU5IDEuOTM3IDQuOTA2IDMuMjgyIDUuMjU5IDguMTA0IDcuMTYxYzQuMDA3IDEuNTggNC44MjMgMS4yNjYgNS42OTMgMS4xODdzMi44MDctMS4xNDcgMy4yMDItMi4yNTUuMzk1LTIuMDU3LjI3Ny0yLjI1NWMtLjExOS0uMTk4LS40MzUtLjMxNi0uOTA5LS41NTRzLTIuODA3LTEuMzg1LTMuMjQyLTEuNTQzLS43NTEtLjIzNy0xLjA2OC4yMzhjLS4zMTYuNDc0LTEuMjI1IDEuNTQzLTEuNTAyIDEuODU5cy0uNTU0LjM1Ny0xLjAyOC4xMTktMi4wMDItLjczOC0zLjgxNS0yLjM1NGMtMS40MS0xLjI1Ny0yLjM2Mi0yLjgxLTIuNjM5LTMuMjg1LS4yNzctLjQ3NC0uMDMtLjczMS4yMDgtLjk2OC4yMTMtLjIxMy40NzQtLjU1NC43MTItLjgzMS4yMzctLjI3Ny4zMTYtLjQ3NS40NzQtLjc5MXMuMDc5LS41OTQtLjA0LS44MzFjLS4xMTctLjIzOC0xLjAzOS0yLjU4NC0xLjQ2MS0zLjUyMiIvPjwvc3ZnPg=="},"displayName":"WhatsApp Business Cloud","typeVersion":1,"nodeCategories":[{"id":6,"name":"Communication"},{"id":28,"name":"HITL"}]},{"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":1235,"icon":"file:extractFromFile.svg","name":"n8n-nodes-base.extractFromFile","codex":{"data":{"alias":["CSV","Spreadsheet","Excel","xls","xlsx","ods","tabular","decode","decoding","Move Binary Data","Binary","File","PDF","JSON","HTML","ICS","iCal","txt","Text","RTF","XML","64","Base64","Convert"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.extractfromfile/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Files","Data Transformation"]}}},"group":"[\"input\"]","defaults":{"name":"Extract from File"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAuOTM3NSAyQzAuNDE5NzMzIDIgMCAyLjQxOTczIDAgMi45Mzc1VjM3LjMyMjFDMCAzNy44Mzk5IDAuNDE5NzMzIDM4LjI1OTYgMC45Mzc1IDM4LjI1OTZIMjYuMjE1NEMyNi43MzMyIDM4LjI1OTYgMjcuMTUyOSAzNy44Mzk5IDI3LjE1MjkgMzcuMzIyMUwyNy4xNTI5IDMwLjY3MTlMMTYuNzk2OSAzMC42NzE5QzE0Ljg5ODQgMzAuNjcxOSAxMy4zNTk0IDI5LjEzMjkgMTMuMzU5NCAyNy4yMzQ0VjI1LjM1OTRDMTMuMzU5NCAyMy40NjA5IDE0Ljg5ODQgMjEuOTIxOSAxNi43OTY5IDIxLjkyMTlIMjcuMTUyOUwyNy4xNTI5IDE1Ljc4MjFIMTQuMzA4M0MxMy43OTA2IDE1Ljc4MjEgMTMuMzcwOCAxNS4zNjI0IDEzLjM3MDggMTQuODQ0NlYySDAuOTM3NVoiIGZpbGw9IiMzNTNGNkUiLz4KPHBhdGggZD0iTTE2LjAyNzEgMkMxNS45NDA4IDIgMTUuODcwOCAyLjA2OTk2IDE1Ljg3MDggMi4xNTYyNVYxMi44MTM0QzE1Ljg3MDggMTMuMDcyMyAxNi4wODA3IDEzLjI4MjEgMTYuMzM5NiAxMy4yODIxSDI2Ljk5NjdDMjcuMDgzIDEzLjI4MjEgMjcuMTUyOSAxMy4yMTIyIDI3LjE1MjkgMTMuMTI1OUwyNy4xNTI5IDEyLjYxNzFDMjcuMTUyOSAxMi4zNjg4IDI3LjA1NDUgMTIuMTMwNyAyNi44NzkxIDExLjk1NUwxNy4yMjI1IDIuMjc1MzhDMTcuMDQ2NiAyLjA5OTA4IDE2LjgwNzkgMiAxNi41NTg4IDJIMTYuMDI3MVoiIGZpbGw9IiMzNTNGNkUiLz4KPHBhdGggZD0iTTI5Ljc2NDIgMzQuNjUwM0MyOS4wMzQgMzMuOTE2IDI5LjAzNzQgMzIuNzI4OCAyOS43NzE2IDMxLjk5ODZMMzMuNjE5NyAyOC4xNzE5TDE2Ljc5NjkgMjguMTcxOUMxNi4yNzkxIDI4LjE3MTkgMTUuODU5NCAyNy43NTIxIDE1Ljg1OTQgMjcuMjM0NFYyNS4zNTk0QzE1Ljg1OTQgMjQuODQxNiAxNi4yNzkxIDI0LjQyMTkgMTYuNzk2OSAyNC40MjE5TDMzLjU0MTIgMjQuNDIxOUwyOS43NzE2IDIwLjY3MzNDMjkuMDM3NCAxOS45NDMxIDI5LjAzNCAxOC43NTU5IDI5Ljc2NDIgMTguMDIxNkMzMC40OTQ0IDE3LjI4NzQgMzEuNjgxNiAxNy4yODQgMzIuNDE1OSAxOC4wMTQyTDM5LjQ0NzEgMjUuMDA2NEMzOS44MDEgMjUuMzU4MyA0MCAyNS44MzY4IDQwIDI2LjMzNTlDNDAgMjYuODM1IDM5LjgwMSAyNy4zMTM1IDM5LjQ0NzEgMjcuNjY1NUwzMi40MTU5IDM0LjY1NzZDMzEuNjgxNiAzNS4zODc4IDMwLjQ5NDQgMzUuMzg0NSAyOS43NjQyIDM0LjY1MDNaIiBmaWxsPSIjMzUzRjZFIi8+Cjwvc3ZnPgo="},"displayName":"Extract from File","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":1260,"icon":"file:whatsapp.svg","name":"n8n-nodes-base.whatsAppTrigger","codex":{"data":{"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.whatsapptrigger/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/whatsapp/"}]},"categories":["Communication"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"trigger\"]","defaults":{"name":"WhatsApp Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0ibTQuODY4IDQzLjMwMyAyLjY5NC05LjgzNWExOC45NCAxOC45NCAwIDAgMS0yLjUzNS05LjQ4OUM1LjAzMiAxMy41MTQgMTMuNTQ4IDUgMjQuMDE0IDVhMTguODcgMTguODcgMCAwIDEgMTMuNDMgNS41NjZBMTguODcgMTguODcgMCAwIDEgNDMgMjMuOTk0Yy0uMDA0IDEwLjQ2NS04LjUyMiAxOC45OC0xOC45ODYgMTguOThoLS4wMDhhMTkgMTkgMCAwIDEtOS4wNzMtMi4zMTF6Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTQuODY4IDQzLjgwM2EuNS41IDAgMCAxLS40ODItLjYzMWwyLjYzOS05LjYzNmExOS41IDE5LjUgMCAwIDEtMi40OTctOS41NTZDNC41MzIgMTMuMjM4IDEzLjI3MyA0LjUgMjQuMDE0IDQuNWExOS4zNyAxOS4zNyAwIDAgMSAxMy43ODQgNS43MTNBMTkuMzYgMTkuMzYgMCAwIDEgNDMuNSAyMy45OTRjLS4wMDQgMTAuNzQxLTguNzQ2IDE5LjQ4LTE5LjQ4NiAxOS40OGExOS41NCAxOS41NCAwIDAgMS05LjE0NC0yLjI3N2wtOS44NzUgMi41ODlhLjUuNSAwIDAgMS0uMTI3LjAxNyIvPjxwYXRoIGZpbGw9IiNjZmQ4ZGMiIGQ9Ik0yNC4wMTQgNWExOC44NyAxOC44NyAwIDAgMSAxMy40MyA1LjU2NkExOC44NyAxOC44NyAwIDAgMSA0MyAyMy45OTRjLS4wMDQgMTAuNDY1LTguNTIyIDE4Ljk4LTE4Ljk4NiAxOC45OGgtLjAwOGExOSAxOSAwIDAgMS05LjA3My0yLjMxMWwtMTAuMDY1IDIuNjQgMi42OTQtOS44MzVhMTguOTQgMTguOTQgMCAwIDEtMi41MzUtOS40ODlDNS4wMzIgMTMuNTE0IDEzLjU0OCA1IDI0LjAxNCA1bTAtMUMxMi45OTggNCA0LjAzMiAxMi45NjIgNC4wMjcgMjMuOTc5YTIwIDIwIDAgMCAwIDIuNDYxIDkuNjIyTDMuOTAzIDQzLjA0YS45OTguOTk4IDAgMCAwIDEuMjE5IDEuMjMxbDkuNjg3LTIuNTRhMjAgMjAgMCAwIDAgOS4xOTcgMi4yNDRjMTEuMDI0IDAgMTkuOTktOC45NjMgMTkuOTk1LTE5Ljk4QTE5Ljg2IDE5Ljg2IDAgMCAwIDM4LjE1MyA5Ljg2IDE5Ljg3IDE5Ljg3IDAgMCAwIDI0LjAxNCA0Ii8+PHBhdGggZmlsbD0iIzQwYzM1MSIgZD0iTTM1LjE3NiAxMi44MzJhMTUuNjcgMTUuNjcgMCAwIDAtMTEuMTU3LTQuNjI2Yy04LjcwNCAwLTE1Ljc4MyA3LjA3Ni0xNS43ODcgMTUuNzc0YTE1Ljc0IDE1Ljc0IDAgMCAwIDIuNDEzIDguMzk2bC4zNzYuNTk3LTEuNTk1IDUuODIxIDUuOTczLTEuNTY2LjU3Ny4zNDJhMTUuNzUgMTUuNzUgMCAwIDAgOC4wMzIgMi4xOTloLjAwNmM4LjY5OCAwIDE1Ljc3Ny03LjA3NyAxNS43OC0xNS43NzZhMTUuNjggMTUuNjggMCAwIDAtNC42MTgtMTEuMTYxIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTE5LjI2OCAxNi4wNDVjLS4zNTUtLjc5LS43MjktLjgwNi0xLjA2OC0uODItLjI3Ny0uMDEyLS41OTMtLjAxMS0uOTA5LS4wMTFzLS44My4xMTktMS4yNjUuNTk0LTEuNjYxIDEuNjIyLTEuNjYxIDMuOTU2IDEuNyA0LjU5IDEuOTM3IDQuOTA2IDMuMjgyIDUuMjU5IDguMTA0IDcuMTYxYzQuMDA3IDEuNTggNC44MjMgMS4yNjYgNS42OTMgMS4xODdzMi44MDctMS4xNDcgMy4yMDItMi4yNTUuMzk1LTIuMDU3LjI3Ny0yLjI1NWMtLjExOS0uMTk4LS40MzUtLjMxNi0uOTA5LS41NTRzLTIuODA3LTEuMzg1LTMuMjQyLTEuNTQzLS43NTEtLjIzNy0xLjA2OC4yMzhjLS4zMTYuNDc0LTEuMjI1IDEuNTQzLTEuNTAyIDEuODU5cy0uNTU0LjM1Ny0xLjAyOC4xMTktMi4wMDItLjczOC0zLjgxNS0yLjM1NGMtMS40MS0xLjI1Ny0yLjM2Mi0yLjgxLTIuNjM5LTMuMjg1LS4yNzctLjQ3NC0uMDMtLjczMS4yMDgtLjk2OC4yMTMtLjIxMy40NzQtLjU1NC43MTItLjgzMS4yMzctLjI3Ny4zMTYtLjQ3NS40NzQtLjc5MXMuMDc5LS41OTQtLjA0LS44MzFjLS4xMTctLjIzOC0xLjAzOS0yLjU4NC0xLjQ2MS0zLjUyMiIvPjwvc3ZnPg=="},"displayName":"WhatsApp Trigger","typeVersion":1,"nodeCategories":[{"id":6,"name":"Communication"}]}],"categories":[{"id":17,"name":"HR"},{"id":47,"name":"AI Chatbot"}],"image":[]}}