{"workflow":{"id":13677,"name":"Watch GitHub releases and Docker tags, analyze changelogs with Claude Haiku, and send update digests to Slack, Discord, Telegram, and ntfy","views":22,"recentViews":0,"totalViews":22,"createdAt":"2026-02-25T01:04:43.839Z","description":"## This n8n template monitors your self-hosted apps for new releases across GitHub and container registries, uses Claude AI to analyze changelogs, and delivers color-coded update digests to Discord, Telegram, Slack, and ntfy.\n\nStop finding out about updates after something breaks. Claude reads every changelog and tells you exactly what changed, what might break, and how urgent the update is — with a ready-to-run Docker update command for each release.\n\n**Good to know**\n\n- Estimated cost is $0.01–0.03 per daily run using Claude Haiku. See [Anthropic pricing](https://www.anthropic.com/pricing) for current rates.\n- PostgreSQL logging is included but optional — the core workflow runs without a database using n8n's built-in static data for version tracking.\n- Test mode is ON by default. Your first run pushes a sample release through the full pipeline (AI analysis, formatting, delivery) so you can verify everything works before going live.\n\n## How it works\n\n- Schedule trigger runs daily at 8 AM and builds a watchlist from your manually configured repos plus optional docker-compose auto-detection\n- Checks GitHub Releases API for repos and Docker Hub/GHCR tag APIs for container images. Pre-releases and RC/beta tags are automatically filtered out\n- Compares each release against the last known version stored in workflow static data to detect what's genuinely new\n- Claude Haiku reads each changelog and returns a structured summary with breaking change detection, CVE/security scanning, migration steps, and urgency classification (critical/recommended/optional)\n- Alert rules route critical and security updates to instant alerts, while everything else batches into a single daily digest sorted by urgency\n- Formatted messages are delivered to your enabled channels with color-coded embeds, Docker update commands, and links to full release notes\n- Release history is optionally logged to PostgreSQL for tracking update timelines\n\n## How to use\n\n- Add your Anthropic API key as an n8n credential, open the Configure Watcher node to set your channel URLs, and edit the Build Repo Watchlist node to add your repos — that's the minimum to get running\n- Click \"Test workflow\" to push a sample release through the full pipeline and verify delivery\n- Set `test_mode` to `false` and toggle Active — the workflow checks daily and only alerts when new releases are found\n\n## Requirements\n\n- Anthropic API key ([setup guide](https://nxsi.io/guides/anthropic-api-key))\n- At least one delivery channel: Discord webhook ([setup guide](https://nxsi.io/guides/discord-webhook)), Telegram bot, Slack app, or ntfy topic\n- Optional: GitHub Personal Access Token for higher API rate limits ([setup guide](https://nxsi.io/guides/github-personal-access-token))\n- Optional: PostgreSQL for release history logging\n\n## Customizing this workflow\n\n- Add or remove repos and container registries in the Build Repo Watchlist code node — pre-loaded with 10 popular self-hosted apps\n- Enable docker-compose auto-detection to automatically build your watchlist from a compose file URL or pasted content\n- Set per-repo alert rules including urgency overrides, instant alert flags, and channel routing\n- Adjust the schedule, swap delivery channels, or add additional registries","workflow":{"name":"GitHub Release Watcher","nodes":[{"id":"a1b2c3d4","name":"Schedule Trigger","type":"n8n-nodes-base.scheduleTrigger","position":[64,768],"parameters":{"rule":{"interval":[{"field":"cronExpression","expression":"0 8 * * *"}]}},"typeVersion":1.2},{"id":"b2c3d4e5","name":"Build Repo Watchlist","type":"n8n-nodes-base.code","position":[480,592],"parameters":{"jsCode":"// Build repo watchlist — edit repos and registries below\n// Scalar config (token, channels, alerts) lives in \"Configure Watcher\" node\nconst config = $('Configure Watcher').first().json;\n\n// === GITHUB REPOS ===\nconst repos = [\n  { owner: 'immich-app', repo: 'immich', label: 'Immich', category: 'media', source: 'github', dependsOn: ['postgres', 'redis'] },\n  { owner: 'jellyfin', repo: 'jellyfin', label: 'Jellyfin', category: 'media', source: 'github', dependsOn: [] },\n  { owner: 'dani-garcia', repo: 'vaultwarden', label: 'Vaultwarden', category: 'security', source: 'github', dependsOn: [] },\n  { owner: 'traefik', repo: 'traefik', label: 'Traefik', category: 'networking', source: 'github', dependsOn: [] },\n  { owner: 'louislam', repo: 'uptime-kuma', label: 'Uptime Kuma', category: 'monitoring', source: 'github', dependsOn: [] },\n  { owner: 'n8n-io', repo: 'n8n', label: 'n8n', category: 'automation', source: 'github', dependsOn: ['postgres', 'redis'] },\n  { owner: 'containrrr', repo: 'watchtower', label: 'Watchtower', category: 'management', source: 'github', dependsOn: [] },\n  { owner: 'nextcloud', repo: 'server', label: 'Nextcloud', category: 'productivity', source: 'github', dependsOn: ['postgres', 'redis'] },\n  { owner: 'grafana', repo: 'grafana', label: 'Grafana', category: 'monitoring', source: 'github', dependsOn: [] },\n  { owner: 'prometheus', repo: 'prometheus', label: 'Prometheus', category: 'monitoring', source: 'github', dependsOn: [] },\n];\n\n// === CONTAINER REGISTRIES (Docker Hub / GHCR) ===\nconst registries = [\n  { registry: 'dockerhub', namespace: 'linuxserver', image: 'heimdall', label: 'Heimdall (Docker)', category: 'management', source: 'dockerhub', dependsOn: [] },\n  { registry: 'dockerhub', namespace: 'linuxserver', image: 'wireguard', label: 'WireGuard (Docker)', category: 'networking', source: 'dockerhub', dependsOn: [] },\n];\n\n// === ALERT OVERRIDES (per-repo) ===\n// Default alert rules come from Configure Watcher node\nvar defaultChannels = config.default_channels ? config.default_channels.split(',').map(function(c) { return c.trim(); }) : ['discord'];\nvar defaultAlertRules = {\n  urgencyOverride: null,\n  channels: defaultChannels,\n  instantAlert: config.default_instant_alert === true\n};\n\nvar alertOverrides = {\n  'dani-garcia/vaultwarden': { urgencyOverride: null, channels: ['discord', 'telegram', 'ntfy'], instantAlert: true },\n  'traefik/traefik': { urgencyOverride: null, channels: ['discord', 'telegram'], instantAlert: true },\n};\n\n// Build output items\nvar ghItems = repos.map(function(r) {\n  return {\n    json: {\n      owner: r.owner, repo: r.repo, label: r.label,\n      category: r.category, source: r.source, dependsOn: r.dependsOn,\n      _githubToken: config.github_token || '',\n      alertRules: alertOverrides[r.owner + '/' + r.repo] || defaultAlertRules,\n    }\n  };\n});\n\nvar regItems = registries.map(function(r) {\n  var repoKey = r.registry + ':' + r.namespace + '/' + r.image;\n  var fetchUrl, fetchHeaders;\n  if (r.registry === 'dockerhub') {\n    fetchUrl = 'https://hub.docker.com/v2/repositories/' + r.namespace + '/' + r.image + '/tags?page_size=5&ordering=last_updated';\n    fetchHeaders = {};\n  } else if (r.registry === 'ghcr') {\n    fetchUrl = 'https://ghcr.io/v2/' + r.namespace + '/' + r.image + '/tags/list';\n    fetchHeaders = {};\n  }\n  return {\n    json: {\n      owner: r.namespace, repo: r.image, label: r.label,\n      category: r.category, source: r.source, registry: r.registry,\n      namespace: r.namespace, image: r.image,\n      repoKey: repoKey, fetchUrl: fetchUrl, fetchHeaders: fetchHeaders,\n      dependsOn: r.dependsOn,\n      alertRules: alertOverrides[repoKey] || defaultAlertRules,\n    }\n  };\n});\n\nreturn [...ghItems, ...regItems];"},"typeVersion":2},{"id":"c3d4e5f6","name":"Fetch Latest Release","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","position":[2048,624],"parameters":{"url":"=https://api.github.com/repos/{{ $json.owner }}/{{ $json.repo }}/releases/latest","options":{},"sendHeaders":true,"headerParameters":{"parameters":[{"name":"User-Agent","value":"n8n-release-watcher/1.0"},{"name":"Accept","value":"application/vnd.github+json"},{"name":"=Authorization","value":"={{ $json._githubToken ? 'Bearer ' + $json._githubToken : '' }}"}]}},"typeVersion":4.2},{"id":"d4e5f6a7","name":"Extract Release Data","type":"n8n-nodes-base.code","position":[2240,624],"parameters":{"jsCode":"const repos = $('Deduplicate Watchlist').all().filter(function(i) { return i.json.source === 'github'; });\nconst responses = $input.all();\nconst results = [];\nlet rateLimited = false;\n\nfor (let i = 0; i < repos.length; i++) {\n  const repo = repos[i].json;\n  const res = responses[i] ? responses[i].json : {};\n\n  if (res.message && res.message.includes('rate limit')) {\n    rateLimited = true;\n    continue;\n  }\n\n  if (!res.tag_name) continue;\n  if (res.prerelease === true) continue;\n\n  results.push({\n    json: {\n      owner: repo.owner,\n      repo: repo.repo,\n      label: repo.label,\n      category: repo.category,\n      source: 'github',\n      tagName: res.tag_name,\n      releaseName: res.name || res.tag_name,\n      publishedAt: res.published_at,\n      htmlUrl: res.html_url,\n      changelog: res.body || 'No release notes provided.',\n      alertRules: repo.alertRules,\n      dependsOn: repo.dependsOn || [],\n    }\n  });\n}\n\nif (rateLimited && results.length === 0) {\n  return [{ json: { _rateLimited: true, _error: 'GitHub API rate limit exceeded. Add a GitHub token to the Repo Watchlist node for 5,000 requests/hour.' } }];\n}\n\nif (results.length === 0) {\n  return [{ json: { _noReleases: true } }];\n}\n\nif (rateLimited) {\n  results[0].json._rateLimitWarning = 'Some repos were rate limited. Add a token for reliable monitoring.';\n}\n\nreturn results;"},"typeVersion":2},{"id":"e5f6a7b8","name":"Compare Versions","type":"n8n-nodes-base.code","position":[2704,640],"parameters":{"jsCode":"// --- TEST MODE ---\nconst config = $('Configure Watcher').first().json;\nconst items = $input.all();\nif (config.test_mode === true) {\n  const testItems = items.filter(i => !i.json._noReleases && !i.json._rateLimited && !i.json._noRegistryReleases && i.json.tagName);\n  if (testItems.length > 0) {\n    const d = testItems[0].json;\n    const out = Object.assign({}, d);\n    out.previousTag = '0.0.0-test';\n    out._storageKey = d.source === 'github' ? d.owner + '/' + d.repo : d.source + ':' + d.owner + '/' + d.repo;\n    out._testMode = true;\n    return [{ json: out }];\n  }\n}\n\n// --- NORMAL MODE ---\nconst staticData = $getWorkflowStaticData('global');\nconst newReleases = [];\nconst isFirstRun = !staticData.initialized;\n\nfunction normalizeTag(tag) {\n  if (!tag) return '';\n  return String(tag).trim().replace(/^v/i, '').toLowerCase();\n}\n\nfor (const item of items) {\n  const d = item.json;\n  if (d._noReleases || d._rateLimited || d._noRegistryReleases) continue;\n\n  // Use source-aware key for registry items\n  const key = d.source === 'github'\n    ? d.owner + '/' + d.repo\n    : d.source + ':' + d.owner + '/' + d.repo;\n  const currentTag = normalizeTag(d.tagName);\n  const storedTag = normalizeTag(staticData[key]);\n\n  if (isFirstRun) {\n    staticData[key] = d.tagName;\n    continue;\n  }\n\n  if (storedTag === currentTag) continue;\n\n  const out = Object.assign({}, d);\n  out.previousTag = staticData[key] || null;\n  out._storageKey = key;\n  newReleases.push({ json: out });\n}\n\nif (isFirstRun) {\n  staticData.initialized = true;\n  const seeded = items.filter(function(i) { return !i.json._noReleases && !i.json._rateLimited && !i.json._noRegistryReleases; }).length;\n  return [{ json: { _firstRun: true, reposSeeded: seeded } }];\n}\n\nif (newReleases.length === 0) {\n  const warn = items.find(function(i) { return i.json._rateLimitWarning; });\n  const warnMsg = warn ? warn.json._rateLimitWarning : null;\n  return [{ json: { _noUpdates: true, _rateLimitWarning: warnMsg } }];\n}\n\nreturn newReleases;"},"typeVersion":2},{"id":"f6a7b8c9","name":"Has Updates?","type":"n8n-nodes-base.if","position":[2864,640],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"id":"check-tag","operator":{"type":"string","operation":"exists"},"leftValue":"={{ $json.tagName }}","rightValue":""}]}},"typeVersion":2.3},{"id":"a7b8c9d0","name":"Prep Changelog for AI","type":"n8n-nodes-base.code","position":[3104,672],"parameters":{"jsCode":"const items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const d = item.json;\n  let log = d.changelog || '';\n\n  // Preserve breaking changes section\n  let breakingSection = '';\n  const bMatch = log.match(/(?:#+\\s*)?(?:BREAKING|Breaking Changes?|\\u26a0\\ufe0f)[\\s\\S]*?(?=\\n#{1,3}\\s|\\n\\n---|\\n\\n\\n|$)/i);\n  if (bMatch) {\n    breakingSection = '\\n\\n--- BREAKING CHANGES ---\\n' + bMatch[0].trim();\n  }\n\n  const maxLen = 1500 - breakingSection.length;\n  if (log.length > maxLen) {\n    log = log.substring(0, maxLen).trim() + '\\n\\n[...truncated]';\n  }\n\n  if (breakingSection && log.indexOf('BREAKING') === -1) {\n    log += breakingSection;\n  }\n\n  results.push({ json: { ...d, changelogTruncated: log } });\n}\n\nreturn results;"},"typeVersion":2},{"id":"b8c9d0e1","name":"Claude Haiku","type":"@n8n/n8n-nodes-langchain.lmChatAnthropic","position":[3344,864],"parameters":{"model":{"__rl":true,"mode":"list","value":"claude-haiku-4-5-20251001"},"options":{"temperature":0.2,"maxTokensToSample":1024}},"typeVersion":1.3},{"id":"c9d0e1f2","name":"Analyze Release","type":"@n8n/n8n-nodes-langchain.chainLlm","position":[3296,672],"parameters":{"text":"=Software: {{ $json.label }} ({{ $json.owner }}/{{ $json.repo }})\nVersion: {{ $json.tagName }}{{ $json.previousTag ? ' (previous: ' + $json.previousTag + ')' : '' }}\nSource: {{ $json.source || 'github' }}\nRelease date: {{ $json.publishedAt || 'unknown' }}\n\nRelease notes:\n{{ $json.changelogTruncated }}","batching":{},"messages":{"messageValues":[{"message":"You are a self-hosted software update analyst. Summarize release notes for a homelabber.\n\nFor each release, provide:\n1. A 1-2 sentence summary of what changed (facts only, no marketing)\n2. Whether there are BREAKING CHANGES (yes/no)\n3. If breaking: what specifically breaks and migration steps\n4. Update urgency: \"critical\" (security fix, CVE), \"recommended\" (useful features/bugfixes), or \"optional\" (minor/cosmetic)\n5. Security assessment: flag any CVEs, security patches, vulnerability fixes, or security-related keywords\n6. Top 3 most impactful changes as a brief list\n7. Specific migration/upgrade steps needed (empty string if none)\n\nReturn ONLY valid JSON:\n{\n  \"summary\": \"1-2 sentence summary\",\n  \"breaking\": false,\n  \"breakingDetails\": null,\n  \"urgency\": \"recommended\",\n  \"security\": false,\n  \"securityDetails\": null,\n  \"keyChanges\": [\"change1\", \"change2\", \"change3\"],\n  \"migrationNotes\": \"\"\n}\n\nSecurity rules:\n- If changelog mentions CVE-XXXX-YYYY, set security:true and include CVE numbers in securityDetails\n- If changelog mentions \"security fix\", \"vulnerability\", \"patch\", \"XSS\", \"SQL injection\", \"auth bypass\", set security:true\n- Security issues automatically make urgency \"critical\" unless they are minor/low-severity\n\nBe concise. Homelabbers want facts, not marketing language."}]},"promptType":"define"},"typeVersion":1.7},{"id":"d0e1f2a3","name":"Parse AI Response","type":"n8n-nodes-base.code","position":[3616,672],"parameters":{"jsCode":"const items = $input.all();\nconst repos = $('Prep Changelog for AI').all();\nconst results = [];\n\nfor (let i = 0; i < items.length; i++) {\n  const aiText = items[i].json.text || items[i].json.output || '';\n  const repoData = repos[i] ? repos[i].json : {};\n\n  let analysis;\n  try {\n    const jsonMatch = aiText.match(/\\{[\\s\\S]*\\}/);\n    analysis = jsonMatch ? JSON.parse(jsonMatch[0]) : null;\n  } catch (e) {\n    analysis = null;\n  }\n\n  if (!analysis) {\n    analysis = {\n      summary: aiText.substring(0, 200),\n      breaking: false,\n      breakingDetails: null,\n      urgency: 'optional',\n      security: false,\n      securityDetails: null,\n      keyChanges: [],\n      migrationNotes: ''\n    };\n  }\n\n  // If security detected and urgency not already critical, escalate\n  if (analysis.security === true && analysis.urgency !== 'critical') {\n    analysis.urgency = 'critical';\n  }\n\n  results.push({\n    json: {\n      owner: repoData.owner,\n      repo: repoData.repo,\n      label: repoData.label,\n      category: repoData.category,\n      source: repoData.source || 'github',\n      tagName: repoData.tagName,\n      previousTag: repoData.previousTag || null,\n      htmlUrl: repoData.htmlUrl,\n      publishedAt: repoData.publishedAt,\n      changelog: repoData.changelog || repoData.changelogTruncated || '',\n      summary: analysis.summary,\n      breaking: analysis.breaking === true,\n      breakingDetails: analysis.breakingDetails || null,\n      urgency: analysis.urgency || 'optional',\n      security: analysis.security === true,\n      securityDetails: analysis.securityDetails || null,\n      keyChanges: analysis.keyChanges || [],\n      migrationNotes: analysis.migrationNotes || '',\n      alertRules: repoData.alertRules || { channels: ['discord', 'telegram'], instantAlert: false },\n      dependsOn: repoData.dependsOn || [],\n      _storageKey: repoData._storageKey || (repoData.owner + '/' + repoData.repo),\n    }\n  });\n}\n\nreturn results;"},"typeVersion":2},{"id":"e1f2a3b4","name":"Format Digest","type":"n8n-nodes-base.code","position":[4272,784],"parameters":{"jsCode":"const items = $input.all();\nconst _cfg = $('Configure Watcher').first().json;\nconst allWatchlist = $('Deduplicate Watchlist').all();\nconst repoCount = allWatchlist.length;\nconst now = new Date();\nconst dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });\n\nconst order = { critical: 0, recommended: 1, optional: 2 };\nconst sorted = [...items].sort(function(a, b) {\n  const ao = order[a.json.finalUrgency || a.json.urgency] ?? 2;\n  const bo = order[b.json.finalUrgency || b.json.urgency] ?? 2;\n  if (ao !== bo) return ao - bo;\n  return (a.json.label || '').localeCompare(b.json.label || '');\n});\n\nconst emoji = { critical: '\\ud83d\\udd34', recommended: '\\ud83d\\udfe0', optional: '\\ud83d\\udd35' };\nconst lbl = { critical: 'CRITICAL', recommended: 'RECOMMENDED', optional: 'OPTIONAL' };\n\nconst dependents = {};\nfor (const w of allWatchlist) {\n  var deps = w.json.dependsOn || [];\n  for (var di = 0; di < deps.length; di++) {\n    if (!dependents[deps[di]]) dependents[deps[di]] = [];\n    dependents[deps[di]].push(w.json.label || w.json.repo);\n  }\n}\n\nconst dockerUpdateMap = {\n  'immich-app/immich': 'docker compose pull && docker compose up -d',\n  'dani-garcia/vaultwarden': 'docker pull vaultwarden/server:TAG && docker compose up -d',\n  'traefik/traefik': 'docker pull traefik:TAG && docker compose up -d',\n  'louislam/uptime-kuma': 'docker pull louislam/uptime-kuma:TAG && docker compose up -d',\n  'n8n-io/n8n': 'docker pull n8nio/n8n:TAG && docker compose up -d',\n  'grafana/grafana': 'docker pull grafana/grafana:TAG && docker compose up -d',\n  'containrrr/watchtower': 'docker pull containrrr/watchtower:TAG && docker compose up -d',\n};\n\nfunction getUpdateCommand(d) {\n  if (d.source === 'dockerhub') return 'docker pull ' + (d.namespace || d.owner) + '/' + (d.image || d.repo) + ':' + d.tagName + ' && docker compose up -d';\n  if (d.source === 'ghcr') return 'docker pull ghcr.io/' + (d.namespace || d.owner) + '/' + (d.image || d.repo) + ':' + d.tagName + ' && docker compose up -d';\n  var key = d.owner + '/' + d.repo;\n  var cmd = dockerUpdateMap[key];\n  if (cmd) return cmd.replace(/TAG/g, d.tagName);\n  return '# Check ' + key + ' release notes for upgrade instructions';\n}\n\nconst counts = { critical: 0, recommended: 0, optional: 0 };\nfor (var ci = 0; ci < sorted.length; ci++) {\n  var urg = sorted[ci].json.finalUrgency || sorted[ci].json.urgency || 'optional';\n  counts[urg] = (counts[urg] ?? 0) + 1;\n}\n\nvar text = '\\ud83d\\udce6 Stack Update Digest \\u2014 ' + dateStr + '\\n';\ntext += repoCount + ' sources checked, ' + sorted.length + ' update' + (sorted.length !== 1 ? 's' : '') + ' found.\\n\\n';\n\nvar slackText = '*\\ud83d\\udce6 Stack Update Digest* \\u2014 ' + dateStr + '\\n' + repoCount + ' sources checked, ' + sorted.length + ' updates\\n\\n';\nvar embeds = [];\n\nfor (var si = 0; si < sorted.length; si++) {\n  var d = sorted[si].json;\n  var urgency = d.finalUrgency || d.urgency || 'optional';\n  var e = emoji[urgency] ?? '\\ud83d\\udd35';\n  var l = lbl[urgency] ?? 'OPTIONAL';\n  var sourceTag = d.source !== 'github' ? ' [' + d.source.toUpperCase() + ']' : '';\n  var updateCmd = getUpdateCommand(d);\n\n  text += e + ' ' + l + ': ' + d.label + ' ' + d.tagName + sourceTag;\n  if (d.previousTag) text += ' (was ' + d.previousTag + ')';\n  text += '\\n   ' + d.summary + '\\n';\n  if (d.breaking && d.breakingDetails) text += '   \\u26a0\\ufe0f BREAKING: ' + d.breakingDetails + '\\n';\n  if (d.security && d.securityDetails) text += '   \\ud83d\\udee1\\ufe0f SECURITY: ' + d.securityDetails + '\\n';\n  if (d.keyChanges && d.keyChanges.length > 0) text += '   Key changes: ' + d.keyChanges.join('; ') + '\\n';\n  if (d.migrationNotes) text += '   \\ud83d\\udccb Migration: ' + d.migrationNotes + '\\n';\n  text += '   \\ud83d\\udcbb `' + updateCmd + '`\\n';\n  var repoName = d.repo || d.image;\n  if (dependents[repoName] && dependents[repoName].length > 0) {\n    text += '   \\ud83d\\udd17 Heads up: ' + dependents[repoName].join(', ') + ' depend on ' + repoName + '\\n';\n  }\n  text += '   \\u2192 ' + d.htmlUrl + '\\n\\n';\n\n  slackText += e + ' *' + l + ': ' + d.label + ' ' + d.tagName + '*' + sourceTag + '\\n';\n  slackText += d.summary + '\\n';\n  if (d.security) slackText += '\\ud83d\\udee1\\ufe0f ' + (d.securityDetails || 'Security update') + '\\n';\n  slackText += '```' + updateCmd + '```\\n<' + d.htmlUrl + '|Release Notes>\\n\\n';\n\n  var color = urgency === 'critical' ? 16711680 : urgency === 'recommended' ? 16750848 : 3381503;\n  var desc = d.summary;\n  if (d.breaking) desc += '\\n\\n\\u26a0\\ufe0f **BREAKING:** ' + (d.breakingDetails || 'See release notes');\n  if (d.security) desc += '\\n\\n\\ud83d\\udee1\\ufe0f **SECURITY:** ' + (d.securityDetails || 'Security-related');\n  if (d.keyChanges && d.keyChanges.length > 0) desc += '\\n\\n**Key changes:** ' + d.keyChanges.join(', ');\n  desc += '\\n\\n```\\n' + updateCmd + '\\n```';\n\n  embeds.push({\n    title: e + ' ' + d.label + ' ' + d.tagName + sourceTag,\n    description: d.previousTag ? desc + '\\n\\nPrevious: ' + d.previousTag : desc,\n    url: d.htmlUrl,\n    color: color,\n    footer: { text: l + (d.security ? ' | SECURITY' : '') },\n  });\n}\n\nvar parts = [];\nif (counts.critical > 0) parts.push(counts.critical + ' critical');\nif (counts.recommended > 0) parts.push(counts.recommended + ' recommended');\nif (counts.optional > 0) parts.push(counts.optional + ' optional');\ntext += '\\u2014 ' + parts.join(', ') + '.';\n\nreturn [{\n  json: {\n    digest: text,\n    discordPayload: {\n      content: '\\ud83d\\udce6 **Stack Update Digest** \\u2014 ' + dateStr + ' (' + repoCount + ' sources checked)',\n      embeds: embeds,\n    },\n    telegramMessage: text,\n    slackText: slackText,\n    ntfyPayload: { topic: (_cfg.ntfy_topic || 'release-watcher'), title: 'Stack Update Digest \\u2014 ' + dateStr, message: sorted.length + ' updates: ' + parts.join(', '), priority: counts.critical > 0 ? 4 : 3, tags: counts.critical > 0 ? ['package', 'warning'] : ['package'] },\n    updateCount: sorted.length,\n    urgencyCounts: counts,\n    updates: sorted.map(function(s) {\n      return { key: s.json._storageKey || (s.json.owner + '/' + s.json.repo), tag: s.json.tagName };\n    }),\n  }\n}];"},"typeVersion":2},{"id":"f2a3b4c5","name":"Send Discord","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","position":[4800,576],"parameters":{"url":"={{ $('Configure Watcher').first().json.discord_webhook_url }}","body":"={{ JSON.stringify($json.discordPayload) }}","method":"POST","options":{},"sendBody":true,"contentType":"raw","sendHeaders":true,"rawContentType":"application/json","headerParameters":{"parameters":[{"name":"User-Agent","value":"n8n-release-watcher/1.0"}]}},"typeVersion":4.2},{"id":"a3b4c5d6","name":"Send Telegram","type":"n8n-nodes-base.telegram","onError":"continueRegularOutput","position":[4800,720],"webhookId":"6913131d-329f-401f-b219-50e78028db97","parameters":{"text":"={{ $json.telegramMessage }}","chatId":"={{ $('Configure Watcher').first().json.telegram_chat_id }}","additionalFields":{}},"typeVersion":1.2},{"id":"b4c5d6e7","name":"Update Stored Versions","type":"n8n-nodes-base.code","position":[4800,1216],"parameters":{"jsCode":"const staticData = $getWorkflowStaticData('global');\nconst updates = $input.first().json.updates || [];\n\nfor (const u of updates) {\n  staticData[u.key] = u.tag;\n}\n\nreturn [{ json: { versionsUpdated: updates.length, versions: updates } }];"},"typeVersion":2},{"id":"s1000001","name":"Overview","type":"n8n-nodes-base.stickyNote","position":[-544,192],"parameters":{"width":512,"height":752,"content":"# 📦 GitHub Release Watcher\n\nMonitors GitHub repos and container registries (Docker Hub, GHCR) for new releases. Uses Claude AI to summarize changelogs, flag breaking changes, detect CVEs, and rate urgency. Delivers color-coded digests to Discord, Telegram, Slack, and ntfy.\n\n### How it works\n1. Runs daily at 8 AM (or click **Test workflow** to run now)\n2. Reads your settings from the **Configure Watcher** node\n3. Builds a watchlist from your manual repos + optional docker-compose auto-detection\n4. Checks GitHub Releases API + Docker Hub/GHCR for each repo\n5. Compares against stored versions to find what’s new\n6. Claude Haiku AI reads each changelog and rates urgency\n7. Critical items get instant alerts; everything else batches into a daily digest\n8. Sends to your enabled channels (Discord, Telegram, Slack, ntfy)\n9. Saves all releases to PostgreSQL and updates stored versions\n\n### Quick Start\n1. Add your **Anthropic API key** → [Setup guide](https://www.nxsi.io/guides/anthropic-api-key)\n2. Open **Configure Watcher** → set your channel URLs (see ⚙️ Config sticky)\n3. Open **Build Repo Watchlist** → add/remove repos to monitor\n4. Click **Test workflow** — test mode is ON by default, so you’ll see a full sample alert through the entire pipeline (AI analysis, formatting, channel delivery)\n5. Once verified, set `test_mode` to `false` in **Configure Watcher**\n6. Toggle **Active** → runs daily at 8 AM. Alerts fire only when repos publish new releases, so your first real alert may take days or weeks\n\n📖 [Prerequisites & API key reference](https://www.nxsi.io/guides)"},"typeVersion":1},{"id":"s2000001","name":"Config Section","type":"n8n-nodes-base.stickyNote","position":[0,-80],"parameters":{"color":7,"width":620,"height":1036,"content":"## ⚙️ Configuration\n\nOpen **Configure Watcher** to set everything:\n\n**🔑 GitHub Token** (optional but recommended)\nPersonal access token for higher API rate limits. Without one you get 60 requests/hour; with one you get 5,000. [Setup guide](https://www.nxsi.io/guides/github-personal-access-token)\n\n**📬 Delivery Channels** — enable the ones you use:\n• **Discord** → paste your webhook URL ([How to create one](https://www.nxsi.io/guides/discord-webhook))\n• **Telegram** → paste your chat ID ([Setup guide](https://www.nxsi.io/guides/telegram-bot))\n• **Slack** → paste your channel ID ([Setup guide](https://www.nxsi.io/guides/slack-bot-token))\n• **ntfy** → free push notifications to your phone ([Setup guide](https://www.nxsi.io/guides/ntfy)). The **topic** is just a unique name you pick (e.g. `my-homelab-updates`).\n\n**🐳 Auto-Detection** (optional)\n• `enable_auto_detect` → set `true` to scan a docker-compose file\n• `compose_url` → a direct link to your raw docker-compose.yml (e.g. a GitHub raw URL like `https://raw.githubusercontent.com/you/repo/main/docker-compose.yml`)\n• `compose_content` → OR paste your entire docker-compose.yml text here instead of a URL\n\n**🔔 Default Alert Behavior**\n• `default_channels` → comma-separated list (e.g. `discord,telegram`)\n• `default_instant_alert` → `false` (default) = only critical/security updates send immediately, everything else batches into one daily digest. Set `true` to send every update immediately.\n\n**🧪 Test Mode**\n• `test_mode` → `true` (default) = click **Test workflow** to see a full sample alert without waiting for a real release. Set `false` once your channels are verified.\n\nEdit **Build Repo Watchlist** to add repos and set per-repo overrides."},"typeVersion":1},{"id":"s3000001","name":"Detection Section","type":"n8n-nodes-base.stickyNote","position":[1776,256],"parameters":{"color":7,"width":1236,"height":556,"content":"## 🔍 Release Detection\n\nFetches the latest release from each repo:\n• **GitHub repos** → GitHub Releases API (gets full changelogs)\n• **Docker Hub / GHCR** → container tag APIs (gets latest stable tag)\n\nPre-releases and RC/beta tags are automatically filtered out. Results from both sources merge together, then **Compare Versions** checks each against the last known version stored in workflow static data.\n\n**Test mode** (`test_mode: true`)**:** Bypasses version comparison entirely — forces one repo through the full alert pipeline so you can verify everything works on demand.\n\n**Production mode** (`test_mode: false`)**:** First run seeds all current versions without alerts. After that, only actual new releases trigger the pipeline. Since most repos release infrequently, your first real alert may take days or weeks — this is normal."},"typeVersion":1},{"id":"s4000001","name":"AI Section","type":"n8n-nodes-base.stickyNote","position":[3040,416],"parameters":{"color":7,"width":708,"height":400,"content":"## 🧠 AI Analysis\n\nClaude Haiku reads each changelog and returns:\n• 1-2 sentence summary (no marketing fluff)\n• Breaking change detection + migration steps\n• CVE and security vulnerability scanning\n• Urgency rating: **critical** (security/CVE), **recommended** (useful features), or **optional** (minor/cosmetic)\n\nCost: ~$0.01-0.03 per run. Haiku is the cheapest Claude model."},"typeVersion":1},{"id":"s5000001","name":"Delivery Section","type":"n8n-nodes-base.stickyNote","position":[4688,112],"parameters":{"color":7,"width":552,"height":1240,"content":"## 📬 Delivery\n\nFormats color-coded messages sorted by urgency:\n• 🔴 **Critical** — security patches, CVEs (red)\n• 🟠 **Recommended** — useful features, bugfixes (orange)\n• 🔵 **Optional** — minor/cosmetic changes (blue)\n\nEach message includes: summary, breaking change warnings, a ready-to-run docker update command, and a link to the full release notes.\n\n**Channels:**\n• **Discord** — rich embeds via webhook ([setup](https://www.nxsi.io/guides/discord-webhook))\n• **Telegram** — text message via bot API ([setup](https://www.nxsi.io/guides/telegram-bot))\n• **Slack** — formatted message via Slack app ([setup](https://www.nxsi.io/guides/slack-bot-token))\n• **ntfy** — push notification to your phone via [ntfy.sh](https://ntfy.sh) ([setup](https://www.nxsi.io/guides/ntfy))\n\nAfter delivery, **Update Stored Versions** saves the new version tags so you won't get alerted for the same release again."},"typeVersion":1},{"id":"db001001","name":"Prep DB Record","type":"n8n-nodes-base.code","position":[3840,832],"parameters":{"jsCode":"const items = $input.all();\nconst prepItems = $('Prep Changelog for AI').all();\nconst results = [];\n\nfunction esc(val) {\n  if (val === null || val === undefined || val === '') return 'NULL';\n  return \"'\" + String(val).replace(/'/g, \"''\") + \"'\";\n}\n\nfor (let i = 0; i < items.length; i++) {\n  const d = items[i].json;\n  const prep = prepItems[i] ? prepItems[i].json : {};\n\n  const repoKey = d.source === 'github'\n    ? d.owner + '/' + d.repo\n    : d.source + ':' + d.owner + '/' + d.repo;\n  const source = d.source || 'github';\n\n  const query = 'INSERT INTO release_history (repo_key, source, tag_name, previous_tag, release_url, changelog_raw, ai_summary, ai_urgency, has_breaking_changes, breaking_details, security_advisory, advisory_severity, update_command) VALUES ('\n    + esc(repoKey) + ', '\n    + esc(source) + ', '\n    + esc(d.tagName) + ', '\n    + esc(d.previousTag || null) + ', '\n    + esc(d.htmlUrl) + ', '\n    + esc(prep.changelog ? prep.changelog.substring(0, 10000) : (d.changelog ? d.changelog.substring(0, 10000) : null)) + ', '\n    + esc(d.summary) + ', '\n    + esc(d.urgency) + ', '\n    + (d.breaking || false) + ', '\n    + esc(d.breakingDetails) + ', '\n    + (d.security || false) + ', '\n    + esc(d.securityDetails ? 'high' : null) + ', '\n    + esc(d.updateCommand || null)\n    + ') ON CONFLICT DO NOTHING';\n\n  results.push({ json: { _dbQuery: query } });\n}\n\nreturn results;"},"typeVersion":2},{"id":"db001002","name":"Save to DB","type":"n8n-nodes-base.postgres","onError":"continueRegularOutput","position":[4032,832],"parameters":{"query":"={{ $json._dbQuery }}","options":{},"operation":"executeQuery"},"typeVersion":2.5},{"id":"sw001001","name":"Source Router","type":"n8n-nodes-base.switch","position":[1824,640],"parameters":{"rules":{"values":[{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.source }}","rightValue":"github"}]}},{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"operator":{"type":"string","operation":"notEquals"},"leftValue":"={{ $json.source }}","rightValue":"github"}]}}]},"options":{"fallbackOutput":"none"}},"typeVersion":3.2},{"id":"hr001001","name":"Fetch Registry Tags","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","position":[1968,960],"parameters":{"url":"={{ $json.fetchUrl }}","options":{},"sendHeaders":true,"headerParameters":{"parameters":[{"name":"User-Agent","value":"n8n-release-watcher/2.0"},{"name":"Accept","value":"application/json"}]}},"typeVersion":4.2},{"id":"cd001001","name":"Extract Registry Data","type":"n8n-nodes-base.code","position":[2368,960],"parameters":{"jsCode":"const watchlist = $('Deduplicate Watchlist').all().filter(function(i) { return i.json.source !== 'github'; });\nconst responses = $input.all();\nconst results = [];\n\nfor (let i = 0; i < watchlist.length; i++) {\n  const reg = watchlist[i].json;\n  const res = responses[i] ? responses[i].json : {};\n\n  let tagName = null;\n  let publishedAt = null;\n\n  if (reg.registry === 'dockerhub') {\n    // Docker Hub returns {results: [{name, last_updated, ...}]}\n    const tags = res.results || [];\n    // Find first non-latest stable tag\n    const stable = tags.find(function(t) {\n      return t.name !== 'latest' && !t.name.includes('-rc') && !t.name.includes('-beta') && !t.name.includes('-alpha');\n    });\n    if (stable) {\n      tagName = stable.name;\n      publishedAt = stable.last_updated || stable.tag_last_pushed;\n    }\n  } else if (reg.registry === 'ghcr') {\n    // GHCR returns {name, tags: ['v1.0', 'latest', ...]}\n    const tags = res.tags || [];\n    const stable = tags.filter(function(t) {\n      return t !== 'latest' && !t.includes('-rc') && !t.includes('-beta');\n    }).sort().reverse();\n    if (stable.length > 0) tagName = stable[0];\n  }\n\n  if (!tagName) continue;\n\n  results.push({\n    json: {\n      owner: reg.namespace,\n      repo: reg.image,\n      label: reg.label,\n      category: reg.category,\n      source: reg.source,\n      registry: reg.registry,\n      namespace: reg.namespace,\n      image: reg.image,\n      tagName: tagName,\n      releaseName: tagName,\n      publishedAt: publishedAt,\n      htmlUrl: reg.registry === 'dockerhub'\n        ? 'https://hub.docker.com/r/' + reg.namespace + '/' + reg.image\n        : 'https://ghcr.io/' + reg.namespace + '/' + reg.image,\n      changelog: 'Container image tag update. Check source repo for release notes.',\n      alertRules: reg.alertRules,\n      dependsOn: reg.dependsOn || [],\n    }\n  });\n}\n\nif (results.length === 0) {\n  return [{ json: { _noRegistryReleases: true } }];\n}\n\nreturn results;"},"typeVersion":2},{"id":"mg001001","name":"Merge All Sources","type":"n8n-nodes-base.merge","position":[2528,640],"parameters":{},"typeVersion":3.1},{"id":"s6000001","name":"Registry Section","type":"n8n-nodes-base.stickyNote","position":[1952,816],"parameters":{"color":7,"width":540,"height":292,"content":"## 📦 Multi-Registry Support\n\nFetches latest stable tags from Docker Hub and GHCR container registries. Automatically filters out `latest`, `-rc`, `-beta`, and `-alpha` tags. Results merge with GitHub releases before version comparison."},"typeVersion":1},{"id":"ar001001","name":"Apply Alert Rules","type":"n8n-nodes-base.code","position":[3840,672],"parameters":{"jsCode":"const items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const d = item.json;\n  const rules = d.alertRules || { channels: ['discord', 'telegram'], instantAlert: false, urgencyOverride: null };\n\n  const finalUrgency = rules.urgencyOverride || d.urgency || 'optional';\n  const isInstant = rules.instantAlert === true || finalUrgency === 'critical';\n  const channels = rules.channels || ['discord', 'telegram'];\n\n  results.push({\n    json: {\n      ...d,\n      finalUrgency: finalUrgency,\n      isInstant: isInstant,\n      channels: channels,\n    }\n  });\n}\n\nreturn results;"},"typeVersion":2},{"id":"ur001001","name":"Urgency Router","type":"n8n-nodes-base.switch","position":[4048,672],"parameters":{"rules":{"values":[{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"422460dc-8374-485a-a071-12cf0934b0f5","operator":{"type":"boolean","operation":"true"},"leftValue":"={{ $json.isInstant }}","rightValue":true}]}},{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"33e6e5a2-3551-42c7-83f9-242d778dfebe","operator":{"type":"boolean","operation":"false"},"leftValue":"={{ $json.isInstant }}","rightValue":false}]}}]},"options":{"fallbackOutput":"none"}},"typeVersion":3.2},{"id":"fi001001","name":"Format Instant Alert","type":"n8n-nodes-base.code","position":[4272,624],"parameters":{"jsCode":"// Format individual instant alerts (one per critical/instant-flagged release)\nconst _cfg = $('Configure Watcher').first().json;\nconst items = $input.all();\nconst results = [];\n\nconst emoji = { critical: '\\ud83d\\udd34', recommended: '\\ud83d\\udfe0', optional: '\\ud83d\\udd35' };\n\n// Map of update commands\nconst dockerUpdateMap = {\n  'immich-app/immich': 'docker compose pull && docker compose up -d',\n  'dani-garcia/vaultwarden': 'docker pull vaultwarden/server:TAG && docker compose up -d',\n  'traefik/traefik': 'docker pull traefik:TAG && docker compose up -d',\n  'n8n-io/n8n': 'docker pull n8nio/n8n:TAG && docker compose up -d',\n};\n\nfunction getUpdateCommand(d) {\n  if (d.source === 'dockerhub') return 'docker pull ' + d.namespace + '/' + d.image + ':' + d.tagName;\n  if (d.source === 'ghcr') return 'docker pull ghcr.io/' + d.namespace + '/' + d.image + ':' + d.tagName;\n  var key = d.owner + '/' + d.repo;\n  var cmd = dockerUpdateMap[key];\n  if (cmd) return cmd.replace(/TAG/g, d.tagName);\n  return '# Check ' + key + ' release notes';\n}\n\nfor (const item of items) {\n  const d = item.json;\n  const e = emoji[d.finalUrgency] || '\\ud83d\\udd34';\n  const sourceTag = d.source !== 'github' ? ' [' + d.source.toUpperCase() + ']' : '';\n  const updateCmd = getUpdateCommand(d);\n\n  let text = e + ' URGENT: ' + d.label + ' ' + d.tagName + sourceTag + '\\n';\n  text += d.summary + '\\n';\n  if (d.breaking) text += '\\u26a0\\ufe0f BREAKING: ' + (d.breakingDetails || 'See release notes') + '\\n';\n  if (d.security) text += '\\ud83d\\udee1\\ufe0f SECURITY: ' + (d.securityDetails || 'Security-related') + '\\n';\n  if (d.keyChanges && d.keyChanges.length > 0) text += 'Key changes: ' + d.keyChanges.join('; ') + '\\n';\n  text += '\\ud83d\\udcbb `' + updateCmd + '`\\n';\n  text += '\\u2192 ' + d.htmlUrl;\n\n  const color = d.finalUrgency === 'critical' ? 16711680 : 16750848;\n  results.push({\n    json: {\n      channels: d.channels,\n      digest: text,\n      telegramMessage: text,\n      discordPayload: {\n        content: e + ' **URGENT UPDATE** \\u2014 ' + d.label,\n        embeds: [{\n          title: d.label + ' ' + d.tagName + sourceTag,\n          description: d.summary + (d.security ? '\\n\\n\\ud83d\\udee1\\ufe0f ' + (d.securityDetails || 'Security update') : '') + '\\n\\n```\\n' + updateCmd + '\\n```',\n          url: d.htmlUrl,\n          color: color,\n          footer: { text: d.finalUrgency.toUpperCase() + (d.security ? ' | SECURITY' : '') }\n        }]\n      },\n      slackText: e + ' *URGENT: ' + d.label + ' ' + d.tagName + '*\\n' + d.summary + (d.security ? '\\n\\ud83d\\udee1\\ufe0f ' + (d.securityDetails || 'Security') : '') + '\\n```' + updateCmd + '```\\n<' + d.htmlUrl + '|Release Notes>',\n      ntfyPayload: { topic: (_cfg.ntfy_topic || 'release-watcher'), title: 'URGENT: ' + d.label + ' ' + d.tagName, message: d.summary + (d.security ? ' [SECURITY]' : ''), priority: d.finalUrgency === 'critical' ? 5 : 4, tags: d.security ? ['warning', 'shield'] : ['warning'] },\n      _storageKey: d._storageKey,\n      tagName: d.tagName,\n      updates: [{ key: d._storageKey || (d.owner + '/' + d.repo), tag: d.tagName }],\n    }\n  });\n}\n\nreturn results;"},"typeVersion":2},{"id":"cr001001","name":"Channel Router","type":"n8n-nodes-base.switch","position":[4496,688],"parameters":{"rules":{"values":[{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"id":"b3627aa2-24a9-4a3f-bbed-350cd836b7f5","operator":{"type":"string","operation":"exists"},"leftValue":"={{ $json.discordPayload.content }}","rightValue":""}]}},{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"id":"5d40425a-690d-496b-9c15-77e4ee21d06e","operator":{"type":"string","operation":"exists"},"leftValue":"={{ $json.telegramMessage }}","rightValue":""}]}},{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"id":"bf4db3ec-c79b-4f56-b64e-e98ca3347dd8","operator":{"type":"string","operation":"exists"},"leftValue":"={{ $json.updates[0].key }}","rightValue":""}]}}]},"options":{"allMatchingOutputs":true},"looseTypeValidation":true},"typeVersion":3.2},{"id":"sl001001","name":"Send Slack","type":"n8n-nodes-base.slack","onError":"continueRegularOutput","position":[4800,896],"webhookId":"f908fc73-e8ec-4715-b860-daef412d5f62","parameters":{"text":"={{ $json.slackText || $json.digest }}","select":"channel","channelId":{"__rl":true,"mode":"id","value":"={{ $('Configure Watcher').first().json.slack_channel_id }}"},"otherOptions":{}},"typeVersion":2.2},{"id":"nt001001","name":"Send ntfy","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","position":[4800,1072],"parameters":{"url":"=https://ntfy.sh/{{ $json.ntfyPayload ? $json.ntfyPayload.topic : 'release-watcher' }}","body":"={{ $json.ntfyPayload ? $json.ntfyPayload.message : $json.digest }}","method":"POST","options":{},"sendBody":true,"contentType":"raw","sendHeaders":true,"rawContentType":"text/plain","headerParameters":{"parameters":[{"name":"Title","value":"={{ $json.ntfyPayload ? $json.ntfyPayload.title : 'Stack Update Digest' }}"},{"name":"Priority","value":"={{ $json.ntfyPayload ? String($json.ntfyPayload.priority) : '3' }}"},{"name":"Tags","value":"={{ $json.ntfyPayload && $json.ntfyPayload.tags ? $json.ntfyPayload.tags.join(',') : 'package' }}"}]}},"typeVersion":4.2},{"id":"s7000001","name":"Routing Section","type":"n8n-nodes-base.stickyNote","position":[3776,256],"parameters":{"color":7,"width":844,"height":736,"content":"## 🚦 Alert Routing\n\nHere's what happens after AI analysis:\n\n**1. Apply Alert Rules**\nChecks each update against your per-repo settings from Build Repo Watchlist. Adds which channels to notify, any urgency overrides, and whether to send instantly or batch.\n\n**2. Urgency Router** — splits into two paths:\n• ⚡ **Instant** → critical/security updates + repos with `instantAlert: true` → sent immediately\n• 📋 **Batch** → everything else → combined into one daily digest message\n\n**3. Save to DB**\nEvery release is saved to the PostgreSQL `release_history` table for long-term tracking, regardless of which path it takes.\n\n**4. Channel Router** → sends to your enabled channels, then updates stored versions so you won't be alerted again."},"typeVersion":1},{"id":"cfg001001","name":"Configure Watcher","type":"n8n-nodes-base.set","position":[272,768],"parameters":{"options":{},"assignments":{"assignments":[{"id":"cfg-01","name":"github_token","type":"string","value":""},{"id":"cfg-02","name":"enable_auto_detect","type":"boolean","value":true},{"id":"cfg-03","name":"compose_url","type":"string","value":""},{"id":"cfg-04","name":"compose_content","type":"string","value":""},{"id":"cfg-05","name":"enable_discord","type":"boolean","value":true},{"id":"cfg-06","name":"discord_webhook_url","type":"string","value":"YOUR_WEBHOOK_URL"},{"id":"cfg-07","name":"enable_telegram","type":"boolean","value":false},{"id":"cfg-08","name":"telegram_chat_id","type":"string","value":"YOUR_CHAT_ID"},{"id":"cfg-09","name":"enable_slack","type":"boolean","value":false},{"id":"cfg-10","name":"slack_channel_id","type":"string","value":"YOUR_CHANNEL_ID"},{"id":"cfg-11","name":"enable_ntfy","type":"boolean","value":false},{"id":"cfg-12","name":"ntfy_topic","type":"string","value":"release-watcher"},{"id":"cfg-13","name":"default_channels","type":"string","value":"discord"},{"id":"cfg-14","name":"default_instant_alert","type":"boolean","value":false},{"id":"cfg-15","name":"test_mode","type":"boolean","value":true}]}},"typeVersion":3.4},{"id":"ad001001","name":"Auto-Detect Enabled?","type":"n8n-nodes-base.if","position":[480,768],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"id":"ad-check","operator":{"type":"boolean","operation":"true","singleValue":true},"leftValue":"={{ $json.enable_auto_detect }}","rightValue":true}]}},"typeVersion":2.3},{"id":"ad001002","name":"Skip Auto-Detect","type":"n8n-nodes-base.set","position":[720,656],"parameters":{"options":{},"assignments":{"assignments":[{"id":"skip-01","name":"_noAutoDetected","type":"boolean","value":true}]}},"typeVersion":3.4},{"id":"ad001003","name":"Has Compose URL?","type":"n8n-nodes-base.if","position":[720,816],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"url-check","operator":{"type":"string","operation":"notEquals"},"leftValue":"={{ $json.compose_url }}","rightValue":""}]}},"typeVersion":2.3},{"id":"ad001004","name":"Fetch Compose File","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","position":[944,720],"parameters":{"url":"={{ $json.compose_url }}","options":{"response":{"response":{"responseFormat":"text"}}},"sendHeaders":true,"headerParameters":{"parameters":[{"name":"User-Agent","value":"n8n-release-watcher/2.0"},{"name":"Accept","value":"text/plain, application/x-yaml, */*"}]}},"typeVersion":4.2},{"id":"ad001005","name":"Parse and Map Compose","type":"n8n-nodes-base.code","position":[1168,816],"parameters":{"jsCode":"// Parse docker-compose.yml to auto-detect services\nvar config = $('Configure Watcher').first().json;\n\n// Get compose content from HTTP fetch or pasted content\nvar composeText = '';\ntry {\n  var fetched = $input.all();\n  if (fetched.length > 0) {\n    var f = fetched[0].json;\n    if (typeof f === 'string') composeText = f;\n    else if (f.data) composeText = String(f.data);\n    else if (f.body) composeText = String(f.body);\n    else if (f.response) composeText = String(f.response);\n  }\n} catch (e) {}\n\nif (!composeText && config.compose_content) {\n  composeText = config.compose_content;\n}\n\nif (!composeText) {\n  return [{ json: { _noAutoDetected: true } }];\n}\n\n// Extract image references via regex\nvar imageRegex = /image:\\s*['\"]?([^'\":\\s#]+)(?::([^'\":\\s#]+))?['\"]?/g;\nvar images = [];\nvar match;\nwhile ((match = imageRegex.exec(composeText)) !== null) {\n  images.push({ raw: match[1], tag: match[2] || 'latest' });\n}\n\nif (images.length === 0) {\n  return [{ json: { _noAutoDetected: true } }];\n}\n\n// Lookup table: Docker image name -> GitHub repo / registry info\nvar imageToRepo = {\n  'vaultwarden/server': { owner: 'dani-garcia', repo: 'vaultwarden', label: 'Vaultwarden', category: 'security', source: 'github' },\n  'traefik': { owner: 'traefik', repo: 'traefik', label: 'Traefik', category: 'networking', source: 'github' },\n  'n8nio/n8n': { owner: 'n8n-io', repo: 'n8n', label: 'n8n', category: 'automation', source: 'github' },\n  'jellyfin/jellyfin': { owner: 'jellyfin', repo: 'jellyfin', label: 'Jellyfin', category: 'media', source: 'github' },\n  'ghcr.io/immich-app/immich-server': { owner: 'immich-app', repo: 'immich', label: 'Immich', category: 'media', source: 'github' },\n  'immich-app/immich-server': { owner: 'immich-app', repo: 'immich', label: 'Immich', category: 'media', source: 'github' },\n  'grafana/grafana': { owner: 'grafana', repo: 'grafana', label: 'Grafana', category: 'monitoring', source: 'github' },\n  'prom/prometheus': { owner: 'prometheus', repo: 'prometheus', label: 'Prometheus', category: 'monitoring', source: 'github' },\n  'louislam/uptime-kuma': { owner: 'louislam', repo: 'uptime-kuma', label: 'Uptime Kuma', category: 'monitoring', source: 'github' },\n  'containrrr/watchtower': { owner: 'containrrr', repo: 'watchtower', label: 'Watchtower', category: 'management', source: 'github' },\n  'nextcloud': { owner: 'nextcloud', repo: 'server', label: 'Nextcloud', category: 'productivity', source: 'github' },\n  'homeassistant/home-assistant': { owner: 'home-assistant', repo: 'core', label: 'Home Assistant', category: 'automation', source: 'github' },\n  'ghcr.io/home-assistant/home-assistant': { owner: 'home-assistant', repo: 'core', label: 'Home Assistant', category: 'automation', source: 'github' },\n  'portainer/portainer-ce': { owner: 'portainer', repo: 'portainer', label: 'Portainer', category: 'management', source: 'github' },\n  'gitea/gitea': { owner: 'go-gitea', repo: 'gitea', label: 'Gitea', category: 'development', source: 'github' },\n  'pihole/pihole': { owner: 'pi-hole', repo: 'pi-hole', label: 'Pi-hole', category: 'networking', source: 'github' },\n  'postgres': { owner: 'postgres', repo: 'postgres', label: 'PostgreSQL', category: 'database', source: 'dockerhub', registry: 'dockerhub', namespace: 'library', image: 'postgres' },\n  'redis': { owner: 'redis', repo: 'redis', label: 'Redis', category: 'database', source: 'dockerhub', registry: 'dockerhub', namespace: 'library', image: 'redis' },\n  'caddy': { owner: 'caddyserver', repo: 'caddy', label: 'Caddy', category: 'networking', source: 'github' },\n  'nginx': { owner: 'nginx', repo: 'nginx', label: 'Nginx', category: 'networking', source: 'dockerhub', registry: 'dockerhub', namespace: 'library', image: 'nginx' },\n  'blakeblackshear/frigate': { owner: 'blakeblackshear', repo: 'frigate', label: 'Frigate', category: 'media', source: 'github' },\n  'ghcr.io/blakeblackshear/frigate': { owner: 'blakeblackshear', repo: 'frigate', label: 'Frigate', category: 'media', source: 'github' },\n  'linuxserver/wireguard': { owner: 'linuxserver', repo: 'docker-wireguard', label: 'WireGuard', category: 'networking', source: 'github' },\n  'linuxserver/heimdall': { owner: 'linuxserver', repo: 'Heimdall', label: 'Heimdall', category: 'management', source: 'github' },\n  'adguard/adguardhome': { owner: 'AdguardTeam', repo: 'AdGuardHome', label: 'AdGuard Home', category: 'networking', source: 'github' },\n  'codercom/code-server': { owner: 'coder', repo: 'code-server', label: 'code-server', category: 'development', source: 'github' },\n  'authelia/authelia': { owner: 'authelia', repo: 'authelia', label: 'Authelia', category: 'security', source: 'github' },\n};\n\nvar defaultChannels = config.default_channels ? config.default_channels.split(',').map(function(c) { return c.trim(); }) : ['discord'];\nvar defaultAlertRules = {\n  urgencyOverride: null,\n  channels: defaultChannels,\n  instantAlert: config.default_instant_alert === true\n};\n\nvar results = [];\nvar seen = {};\n\nfor (var i = 0; i < images.length; i++) {\n  var img = images[i];\n  var lookupKey = img.raw;\n  var stripped = lookupKey.replace(/^ghcr\\.io\\//, '').replace(/^docker\\.io\\//, '').replace(/^library\\//, '');\n  var mapped = imageToRepo[lookupKey] || imageToRepo[stripped];\n\n  if (mapped) {\n    var uniqueKey = mapped.source + ':' + mapped.owner + '/' + mapped.repo;\n    if (seen[uniqueKey]) continue;\n    seen[uniqueKey] = true;\n    var item = {\n      owner: mapped.owner, repo: mapped.repo, label: mapped.label,\n      category: mapped.category, source: mapped.source,\n      dependsOn: [], _autoDetected: true,\n      alertRules: defaultAlertRules,\n    };\n    if (mapped.source === 'github') {\n      item._githubToken = config.github_token || '';\n    } else {\n      item.registry = mapped.registry || 'dockerhub';\n      item.namespace = mapped.namespace || mapped.owner;\n      item.image = mapped.image || mapped.repo;\n      item.repoKey = item.registry + ':' + item.namespace + '/' + item.image;\n      if (item.registry === 'dockerhub') {\n        item.fetchUrl = 'https://hub.docker.com/v2/repositories/' + item.namespace + '/' + item.image + '/tags?page_size=5&ordering=last_updated';\n      }\n      item.fetchHeaders = {};\n    }\n    results.push({ json: item });\n  } else {\n    // Unknown image — watch via Docker Hub tag API\n    var parts = stripped.split('/');\n    var ns = parts.length > 1 ? parts[0] : 'library';\n    var im = parts.length > 1 ? parts[1] : parts[0];\n    var uKey = 'dockerhub:' + ns + '/' + im;\n    if (seen[uKey]) continue;\n    seen[uKey] = true;\n    results.push({\n      json: {\n        owner: ns, repo: im,\n        label: im.charAt(0).toUpperCase() + im.slice(1) + ' (auto-detected)',\n        category: 'other', source: 'dockerhub', registry: 'dockerhub',\n        namespace: ns, image: im, repoKey: uKey,\n        fetchUrl: 'https://hub.docker.com/v2/repositories/' + ns + '/' + im + '/tags?page_size=5&ordering=last_updated',\n        fetchHeaders: {}, dependsOn: [], _autoDetected: true,\n        alertRules: defaultAlertRules,\n      }\n    });\n  }\n}\n\nif (results.length === 0) {\n  return [{ json: { _noAutoDetected: true } }];\n}\nreturn results;"},"typeVersion":2},{"id":"ad001006","name":"Merge Manual + Auto","type":"n8n-nodes-base.merge","position":[1376,640],"parameters":{},"typeVersion":3.2},{"id":"ad001007","name":"Deduplicate Watchlist","type":"n8n-nodes-base.code","position":[1584,640],"parameters":{"jsCode":"// Deduplicate manual + auto-detected watchlist items\n// Manual entries (no _autoDetected flag) take priority over auto-detected dupes\nvar items = $input.all();\nvar unique = {};\nvar results = [];\n\nfor (var i = 0; i < items.length; i++) {\n  var d = items[i].json;\n  if (d._noAutoDetected) continue;\n\n  var key;\n  if (d.source === 'github') {\n    key = 'github:' + d.owner + '/' + d.repo;\n  } else {\n    key = (d.registry || d.source) + ':' + (d.namespace || d.owner) + '/' + (d.image || d.repo);\n  }\n\n  if (unique[key]) {\n    if (!d._autoDetected && unique[key]._autoDetected) {\n      unique[key] = d;\n    }\n    continue;\n  }\n  unique[key] = d;\n}\n\nfor (var k in unique) {\n  results.push({ json: unique[k] });\n}\n\nif (results.length === 0) {\n  return [{ json: { _empty: true, source: 'github' } }];\n}\nreturn results;"},"typeVersion":2},{"id":"s9000001","name":"Auto-Detect Section","type":"n8n-nodes-base.stickyNote","position":[640,256],"parameters":{"color":7,"width":1108,"height":704,"content":"## 🐳 Docker Compose Auto-Detection\n\n**What this does:** Reads your docker-compose.yml and automatically adds every service to the watchlist — no manual entry needed.\n\n**How to use it:**\n1. In **Configure Watcher**, set `enable_auto_detect` to `true`\n2. Provide your compose file in ONE of two ways:\n   • **compose_url** — a direct URL to the raw YAML file (GitHub raw link, Gitea, or any URL that returns the file contents)\n   • **compose_content** — paste your entire docker-compose.yml text directly into the field\n\n**What gets recognized:** 25+ common homelab images including Traefik, Immich, Vaultwarden, Grafana, Jellyfin, Home Assistant, Pi-hole, and more are mapped to their GitHub repos for full release notes. Unknown images fall back to Docker Hub tag monitoring.\n\nManual repos in **Build Repo Watchlist** always override auto-detected duplicates."},"typeVersion":1},{"id":"e284c6ea-5915-421e-9f62-1d75a5b57dd9","name":"Warning — Anthropic API Cost","type":"n8n-nodes-base.stickyNote","position":[3200,832],"parameters":{"color":3,"width":464,"height":552,"content":"\n\n\n\n\n\n\n\n\n\n\n\n## 🔑 Anthropic API Key Required\n\nThis workflow uses **Claude Haiku** to analyze changelogs. You need an Anthropic API key to use it.\n\n### Setup:\n1. Go to [console.anthropic.com](https://console.anthropic.com) → **API Keys** → **Create Key**\n2. In n8n: **Credentials** → **Add Credential** → search **Anthropic**\n3. Paste your API key and save\n4. Double-click the **Claude Haiku** node → select your new credential\n\n📖 [Full Anthropic API setup guide →](https://www.nxsi.io/guides/anthropic-api-key)\n📖 [Claude API setup for n8n →](https://www.nxsi.io/guides/claude-api-setup)\n\n💰 **Cost:** ~$0.25 per million input tokens. A typical run monitoring 10 repos costs about $0.01-0.03."},"typeVersion":1}],"settings":{"timezone":"America/Chicago","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"executionOrder":"v1","saveExecutionProgress":true,"saveDataSuccessExecution":"all"},"connections":{"Claude Haiku":{"ai_languageModel":[[{"node":"Analyze Release","type":"ai_languageModel","index":0}]]},"Has Updates?":{"main":[[{"node":"Prep Changelog for AI","type":"main","index":0}]]},"Format Digest":{"main":[[{"node":"Channel Router","type":"main","index":0}]]},"Source Router":{"main":[[{"node":"Fetch Latest Release","type":"main","index":0}],[{"node":"Fetch Registry Tags","type":"main","index":0}]]},"Channel Router":{"main":[[{"node":"Send Discord","type":"main","index":0},{"node":"Send Slack","type":"main","index":0},{"node":"Send ntfy","type":"main","index":0}],[{"node":"Send Telegram","type":"main","index":0}],[{"node":"Update Stored Versions","type":"main","index":0}]]},"Prep DB Record":{"main":[[{"node":"Save to DB","type":"main","index":0}]]},"Urgency Router":{"main":[[{"node":"Format Instant Alert","type":"main","index":0}],[{"node":"Format Digest","type":"main","index":0}]]},"Analyze Release":{"main":[[{"node":"Parse AI Response","type":"main","index":0}]]},"Compare Versions":{"main":[[{"node":"Has Updates?","type":"main","index":0}]]},"Has Compose URL?":{"main":[[{"node":"Fetch Compose File","type":"main","index":0}],[{"node":"Parse and Map Compose","type":"main","index":0}]]},"Schedule Trigger":{"main":[[{"node":"Configure Watcher","type":"main","index":0}]]},"Skip Auto-Detect":{"main":[[{"node":"Merge Manual + Auto","type":"main","index":1}]]},"Apply Alert Rules":{"main":[[{"node":"Urgency Router","type":"main","index":0}]]},"Configure Watcher":{"main":[[{"node":"Build Repo Watchlist","type":"main","index":0},{"node":"Auto-Detect Enabled?","type":"main","index":0}]]},"Merge All Sources":{"main":[[{"node":"Compare Versions","type":"main","index":0}]]},"Parse AI Response":{"main":[[{"node":"Prep DB Record","type":"main","index":0},{"node":"Apply Alert Rules","type":"main","index":0}]]},"Fetch Compose File":{"main":[[{"node":"Parse and Map Compose","type":"main","index":0}]]},"Fetch Registry Tags":{"main":[[{"node":"Extract Registry Data","type":"main","index":0}]]},"Merge Manual + Auto":{"main":[[{"node":"Deduplicate Watchlist","type":"main","index":0}]]},"Auto-Detect Enabled?":{"main":[[{"node":"Has Compose URL?","type":"main","index":0}],[{"node":"Skip Auto-Detect","type":"main","index":0}]]},"Build Repo Watchlist":{"main":[[{"node":"Merge Manual + Auto","type":"main","index":0}]]},"Extract Release Data":{"main":[[{"node":"Merge All Sources","type":"main","index":0}]]},"Fetch Latest Release":{"main":[[{"node":"Extract Release Data","type":"main","index":0}]]},"Format Instant Alert":{"main":[[{"node":"Channel Router","type":"main","index":0}]]},"Deduplicate Watchlist":{"main":[[{"node":"Source Router","type":"main","index":0}]]},"Extract Registry Data":{"main":[[{"node":"Merge All Sources","type":"main","index":1}]]},"Parse and Map Compose":{"main":[[{"node":"Merge Manual + Auto","type":"main","index":1}]]},"Prep Changelog for AI":{"main":[[{"node":"Analyze Release","type":"main","index":0}]]}}},"lastUpdatedBy":1,"workflowInfo":{"nodeCount":43,"nodeTypes":{"n8n-nodes-base.if":{"count":3},"n8n-nodes-base.set":{"count":2},"n8n-nodes-base.code":{"count":13},"n8n-nodes-base.merge":{"count":2},"n8n-nodes-base.slack":{"count":1},"n8n-nodes-base.switch":{"count":3},"n8n-nodes-base.postgres":{"count":1},"n8n-nodes-base.telegram":{"count":1},"n8n-nodes-base.stickyNote":{"count":9},"n8n-nodes-base.httpRequest":{"count":5},"n8n-nodes-base.scheduleTrigger":{"count":1},"@n8n/n8n-nodes-langchain.chainLlm":{"count":1},"@n8n/n8n-nodes-langchain.lmChatAnthropic":{"count":1}}},"status":"published","readyToDemo":null,"user":{"name":"nXsi","username":"dyllank","bio":"","verified":true,"links":["https://www.nxsi.io"],"avatar":"https://gravatar.com/avatar/05bc1aa9e985cd6db6b0dd88a651831586b48d677475e826dde0ecde2a899325?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":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":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":30,"icon":"file:postgres.svg","name":"n8n-nodes-base.postgres","codex":{"data":{"resources":{"generic":[{"url":"https://n8n.io/blog/love-at-first-sight-ricardos-n8n-journey/","icon":"❤️","label":"Love at first sight: Ricardo’s n8n journey"},{"url":"https://n8n.io/blog/why-i-chose-n8n-over-zapier-in-2020/","icon":"😍","label":"Why I chose n8n over Zapier in 2020"},{"url":"https://n8n.io/blog/database-monitoring-and-alerting-with-n8n/","icon":"📡","label":"Database Monitoring and Alerting 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/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-honest-burgers-use-automation-to-save-100k-per-year/","icon":"🍔","label":"How Honest Burgers Use Automation to Save $100k per year"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.postgres/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/postgres/"}]},"categories":["Development","Data & Storage"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"input\"]","defaults":{"name":"Postgres"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiB2aWV3Qm94PSIwIDAgNzkgODEiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZD0iTTc3LjM5MSA0Ny45MjJjLS40NjYtMS40MTItMS42ODgtMi4zOTYtMy4yNjgtMi42MzItLjc0NS0uMTExLTEuNTk4LS4wNjQtMi42MDguMTQ0LTEuNzYuMzYzLTMuMDY1LjUwMS00LjAxOC41MjggMy41OTYtNi4wNzIgNi41MjEtMTIuOTk3IDguMjA0LTE5LjUxNSAyLjcyMi0xMC41NCAxLjI2OC0xNS4zNDEtLjQzMi0xNy41MTNDNzAuNzcgMy4xODUgNjQuMjA2LjA5NyA1Ni4yODcuMDAyYy00LjIyNC0uMDUyLTcuOTMzLjc4Mi05Ljg2NyAxLjM4MmEzNyAzNyAwIDAgMC01Ljc3LS41MjhjLTMuODA5LS4wNjEtNy4xNzQuNzctMTAuMDUgMi40NzZhNDYgNDYgMCAwIDAtNy4wOTgtMS43ODJDMTYuNTYxLjQxMSAxMC45NjggMS4yOTkgNi44NzYgNC4xOSAxLjkyMiA3LjY4OS0uMzc1IDEzLjc3LjA1IDIyLjI2MmMuMTM1IDIuNjk2IDEuNjQzIDEwLjkgNC4wMTggMTguNjggMS4zNjUgNC40NzIgMi44MiA4LjE4NSA0LjMyNiAxMS4wMzggMi4xMzUgNC4wNDYgNC40MTkgNi40MjggNi45ODQgNy4yODQgMS40MzguNDc5IDQuMDQ5LjgxNCA2Ljc5Ny0xLjQ3M2E2IDYgMCAwIDAgMS40MjkgMS4yM2MuNzgzLjQ5NCAxLjc0Ljg5NyAyLjY5NiAxLjEzNiAzLjQ0Ni44NjIgNi42NzQuNjQ2IDkuNDI3LS41NjFsLjA0MSAxLjM2Mi4wNiAxLjg5OWMuMTYzIDQuMDY0LjQ0IDcuMjIzIDEuMjU5IDkuNDM0LjA0NS4xMjIuMTA1LjMwNy4xNjkuNTAzLjQwOSAxLjI1MSAxLjA5MiAzLjM0NiAyLjgzIDQuOTg3IDEuOCAxLjY5OSAzLjk3OCAyLjIyIDUuOTcyIDIuMjIgMSAwIDEuOTU1LS4xMzEgMi43OTItLjMxMSAyLjk4NC0uNjM5IDYuMzczLTEuNjE0IDguODI0LTUuMTA0IDIuMzE4LTMuMyAzLjQ0NC04LjI3IDMuNjQ4LTE2LjEwMWwuMDc0LS42MzQuMDQ4LS40MTQuNTQ2LjA0OC4xNDEuMDFjMy4wMzkuMTM4IDYuNzU1LS41MDYgOS4wMzctMS41NjYgMS44MDMtLjgzNyA3LjU4Mi0zLjg4OCA2LjIyMS04LjAwNyIvPjxwYXRoIGZpbGw9IiMzMzY3OTEiIGQ9Ik03Mi4xOTUgNDguNzIzYy05LjAzNiAxLjg2NC05LjY1Ny0xLjE5NS05LjY1Ny0xLjE5NSA5LjU0MS0xNC4xNTcgMTMuNTI5LTMyLjEyNyAxMC4wODctMzYuNTI1QzYzLjIzNS0uOTk0IDQ2Ljk4MSA0LjY4IDQ2LjcxIDQuODI3bC0uMDg3LjAxNmMtMS43ODUtLjM3MS0zLjc4My0uNTkxLTYuMDI5LS42MjgtNC4wODktLjA2Ny03LjE5IDEuMDcyLTkuNTQ0IDIuODU3IDAgMC0yOC45OTUtMTEuOTQ1LTI3LjY0NyAxNS4wMjMuMjg3IDUuNzM3IDguMjIzIDQzLjQxIDE3LjY4OSAzMi4wMzEgMy40Ni00LjE2MSA2LjgwMy03LjY3OSA2LjgwMy03LjY3OSAxLjY2IDEuMTAzIDMuNjQ4IDEuNjY2IDUuNzMyIDEuNDYzbC4xNjItLjEzN2E2LjMgNi4zIDAgMCAwIC4wNjUgMS42MmMtMi40MzkgMi43MjUtMS43MjIgMy4yMDMtNi41OTcgNC4yMDYtNC45MzMgMS4wMTctMi4wMzUgMi44MjYtLjE0MyAzLjI5OSAyLjI5NC41NzQgNy42IDEuMzg2IDExLjE4NS0zLjYzM2wtLjE0My41NzNjLjk1Ni43NjUgMS42MjYgNC45NzggMS41MTQgOC43OTdzLS4xODggNi40NDEuNTY1IDguNDg5IDEuNTAzIDYuNjU2IDcuOTEyIDUuMjgyYzUuMzU1LTEuMTQ4IDguMTMtNC4xMjEgOC41MTYtOS4wODEuMjc0LTMuNTI2Ljg5NC0zLjAwNS45MzMtNi4xNThsLjQ5Ny0xLjQ5M2MuNTczLTQuNzguMDkxLTYuMzIyIDMuMzktNS42MDVsLjgwMi4wN2MyLjQyOC4xMSA1LjYwNi0uMzkxIDcuNDcxLTEuMjU3IDQuMDE2LTEuODY0IDYuMzk4LTQuOTc2IDIuNDM4LTQuMTU4Ii8+PHBhdGggZD0iTTMyLjc0NyAyNC42NmMtLjgxNC0uMTEzLTEuNTUyLS4wMDgtMS45MjUuMjc0YS43LjcgMCAwIDAtLjI5Mi40N2MtLjA0Ny4zMzYuMTg4LjcwNy4zMzMuODk4LjQwOS41NDIgMS4wMDYuOTE1IDEuNTk4Ljk5N2EyIDIgMCAwIDAgLjI1Ni4wMThjLjk4NiAwIDEuODgzLS43NjggMS45NjItMS4zMzUuMDk5LS43MS0uOTMyLTEuMTgzLTEuOTMxLTEuMzIybTI2Ljk3NS4wMjJjLS4wNzgtLjU1Ni0xLjA2OC0uNzE1LTIuMDA3LS41ODRzLTEuODQ4LjU1NC0xLjc3MiAxLjExMmMuMDYxLjQzNC44NDQgMS4xNzQgMS43NzEgMS4xNzRxLjExNyAwIC4yMzctLjAxNmMuNjE5LS4wODYgMS4wNzMtLjQ3OSAxLjI4OC0uNzA1LjMyOS0uMzQ1LjUxOC0uNzMuNDg0LS45OG0xNS40NzcgMjMuODI4Yy0uMzQ1LTEuMDQyLTEuNDUzLTEuMzc3LTMuMjk2LS45OTctNS40NzEgMS4xMjktNy40My4zNDctOC4wNzMtLjEyNyA0LjI1Mi02LjQ3OCA3Ljc1LTE0LjMwOCA5LjYzNy0yMS42MTQuODk0LTMuNDYxIDEuMzg4LTYuNjc1IDEuNDI4LTkuMjk0LjA0NS0yLjg3Ni0uNDQ1LTQuOTg4LTEuNDU1LTYuMjc5LTQuMDcyLTUuMjAzLTEwLjA0OC03Ljk5NC0xNy4yODMtOC4wNy00Ljk3My0uMDU2LTkuMTc1IDEuMjE3LTkuOTkgMS41NzVhMjUgMjUgMCAwIDAtNS42MjItLjcyMmMtMy43MzQtLjA2LTYuOTYxLjgzNC05LjYzMyAyLjY1NWE0MyA0MyAwIDAgMC03LjgyOC0yLjA1MmMtNi4zNDItMS4wMjEtMTEuMzgxLS4yNDgtMTQuOTc4IDIuMy00LjI5MSAzLjA0LTYuMjcyIDguNDc1LTUuODg4IDE2LjE1Mi4xMjkgMi41ODMgMS42MDEgMTAuNTI5IDMuOTIzIDE4LjEzOSAzLjA1NyAxMC4wMTYgNi4zOCAxNS42ODYgOS44NzcgMTYuODUyYTQuNCA0LjQgMCAwIDAgMS40MDIuMjMyYzEuMjc2IDAgMi44MzktLjU3NSA0LjQ2Ni0yLjUzMWExNjEgMTYxIDAgMCAxIDYuMTU2LTYuOTY2IDkuOSA5LjkgMCAwIDAgNC40MjkgMS4xOTFsLjAxLjEyMWMtLjMxLjM2OC0uNTY0LjY5LS43ODEuOTY1LTEuMDcgMS4zNTgtMS4yOTMgMS42NDEtNC43MzggMi4zNTEtLjk4LjIwMi0zLjU4Mi43MzgtMy42MiAyLjU2My0uMDQxIDEuOTkzIDMuMDc2IDIuODMgMy40MzEgMi45MTkgMS4yMzguMzEgMi40My40NjMgMy41NjguNDYzIDIuNzY2IDAgNS4yLS45MDkgNy4xNDUtMi42NjgtLjA2IDcuMTA2LjIzNiAxNC4xMDcgMS4wODkgMTYuMjQxLjY5OSAxLjc0NiAyLjQwNiA2LjAxNCA3Ljc5OCA2LjAxNC43OTEgMCAxLjY2Mi0uMDkyIDIuNjItLjI5NyA1LjYyNy0xLjIwNyA4LjA3MS0zLjY5NCA5LjAxNi05LjE3Ny41MDYtMi45MyAxLjM3NC05LjkyOCAxLjc4Mi0xMy42ODIuODYyLjI2OSAxLjk3MS4zOTIgMy4xNy4zOTIgMi41MDEgMCA1LjM4Ny0uNTMxIDcuMTk3LTEuMzcyIDIuMDMzLS45NDQgNS43MDItMy4yNjEgNS4wMzctNS4yNzR6TTYxLjggMjMuMTQ3Yy0uMDE5IDEuMTA4LS4xNzEgMi4xMTQtLjMzMyAzLjE2NC0uMTc0IDEuMTI5LS4zNTQgMi4yOTctLjM5OSAzLjcxNS0uMDQ1IDEuMzc5LjEyOCAyLjgxNC4yOTQgNC4yLjMzNyAyLjgwMS42ODIgNS42ODUtLjY1NSA4LjUzMWExMSAxMSAwIDAgMS0uNTkyLTEuMjE4Yy0uMTY2LS40MDMtLjUyNy0xLjA1LTEuMDI3LTEuOTQ2LTEuOTQ0LTMuNDg3LTYuNDk3LTExLjY1Mi00LjE2Ny0xNC45ODQuNjk0LS45OTIgMi40NTYtMi4wMTEgNi44NzktMS40NjN6TTU2LjQzOSA0LjM3NGM2LjQ4Mi4xNDMgMTEuNjA5IDIuNTY4IDE1LjI0IDcuMjA3IDIuNzg0IDMuNTU4LS4yODIgMTkuNzQ5LTkuMTU4IDMzLjcxNmwtLjI2OS0uMzM5LS4xMTItLjE0YzIuMjk0LTMuNzg4IDEuODQ1LTcuNTM2IDEuNDQ2LTEwLjg1OS0uMTY0LTEuMzY0LS4zMTktMi42NTItLjI4LTMuODYxLjA0MS0xLjI4My4yMS0yLjM4Mi4zNzQtMy40NDYuMjAyLTEuMzExLjQwNy0yLjY2Ny4zNS00LjI2NWExLjggMS44IDAgMCAwIC4wMzctLjYwMWMtLjE0NC0xLjUzMy0xLjg5NC02LjEyLTUuNDYyLTEwLjI3My0xLjk1MS0yLjI3MS00Ljc5Ny00LjgxMy04LjY4Mi02LjUyN2EyOS4zIDI5LjMgMCAwIDEgNi41MTUtLjYxMnpNMjAuMTY3IDUzLjI5OGMtMS43OTMgMi4xNTUtMy4wMzEgMS43NDItMy40MzggMS42MDctMi42NTMtLjg4NS01LjczLTYuNDkxLTguNDQ0LTE1LjM4Mi0yLjM0OC03LjY5My0zLjcyLTE1LjQyOC0zLjgyOS0xNy41OTctLjM0My02Ljg2IDEuMzItMTEuNjQxIDQuOTQzLTE0LjIxIDUuODk2LTQuMTgxIDE1LjU4OS0xLjY3OSAxOS40ODQtLjQwOWwtLjE3LjE2M2MtNi4zOTEgNi40NTUtNi4yNCAxNy40ODMtNi4yMjQgMTguMTU3YTIyIDIyIDAgMCAwIC4wNTEgMS4xMzVjLjExIDEuODU1LjMxNSA1LjMwNy0uMjMyIDkuMjE3LS41MDggMy42MzMuNjEyIDcuMTg5IDMuMDcyIDkuNzU2cS4zODMuMzk4Ljc5NS43NWExNjQgMTY0IDAgMCAwLTYuMDA4IDYuODE0em02LjgzLTkuMTEzYy0xLjk4My0yLjA2OS0yLjg4NC00Ljk0Ny0yLjQ3MS03Ljg5Ni41NzctNC4xMy4zNjQtNy43MjcuMjUtOS42NTlsLS4wMzktLjY5NGMuOTM0LS44MjggNS4yNjEtMy4xNDYgOC4zNDYtMi40MzkgMS40MDguMzIzIDIuMjY2IDEuMjgxIDIuNjIzIDIuOTMxIDEuODQ2IDguNTM5LjI0NCAxMi4wOTgtMS4wNDMgMTQuOTU3LS4yNjUuNTg5LS41MTYgMS4xNDYtLjczIDEuNzIybC0uMTY2LjQ0NWMtLjQyIDEuMTI2LS44MTEgMi4xNzMtMS4wNTMgMy4xNjctMi4xMDgtLjAwNi00LjE1OS0uOTA3LTUuNzE4LTIuNTM0em0uMzI0IDExLjUxNmE1IDUgMCAwIDEtMS40OTQtLjY0MmMuMjcxLS4xMjguNzU0LS4zMDEgMS41OTEtLjQ3NCA0LjA1Mi0uODM0IDQuNjc4LTEuNDIzIDYuMDQ1LTMuMTU4LjMxMy0uMzk4LjY2OS0uODQ5IDEuMTYtMS4zOTguNzMzLS44MjEgMS4wNjgtLjY4MiAxLjY3Ni0uNDMuNDkzLjIwNC45NzIuODIxIDEuMTY3IDEuNTAxLjA5Mi4zMjEuMTk1LjkzLS4xNDMgMS40MDQtMi44NTUgMy45OTctNy4wMTUgMy45NDYtMTAuMDAzIDMuMTk4em0yMS4yMDcgMTkuNzM1Yy00Ljk1NyAxLjA2Mi02LjcxMy0xLjQ2Ny03Ljg2OS00LjM1OS0uNzQ3LTEuODY3LTEuMTEzLTEwLjI4NS0uODUzLTE5LjU4MmExLjEgMS4xIDAgMCAwLS4wNDgtLjM1NiA1IDUgMCAwIDAtLjEzOS0uNjU3Yy0uMzg3LTEuMzUzLTEuMzMxLTIuNDg0LTIuNDYyLTIuOTUzLS40NS0uMTg2LTEuMjc1LS41MjgtMi4yNjctLjI3NC4yMTItLjg3MS41NzgtMS44NTUuOTc2LTIuOTIxbC4xNjctLjQ0OGMuMTg4LS41MDUuNDIzLTEuMDI5LjY3My0xLjU4MyAxLjM0Ny0yLjk5MiAzLjE5Mi03LjA5MSAxLjE5LTE2LjM1LS43NS0zLjQ2OC0zLjI1NC01LjE2MS03LjA1LTQuNzY4LTIuMjc2LjIzNS00LjM1OCAxLjE1NC01LjM5NiAxLjY4cS0uMzM0LjE2OS0uNjE4LjMyOWMuMjktMy40OTQgMS4zODUtMTAuMDI0IDUuNDgxLTE0LjE1NiAyLjU3OS0yLjYwMSA2LjAxNC0zLjg4NiAxMC4xOTktMy44MTcgOC4yNDYuMTM1IDEzLjUzNCA0LjM2NyAxNi41MTggNy44OTMgMi41NzEgMy4wMzkgMy45NjQgNi4xIDQuNTIgNy43NTEtNC4xNzktLjQyNS03LjAyMi40LTguNDYzIDIuNDYtMy4xMzUgNC40ODEgMS43MTUgMTMuMTc4IDQuMDQ2IDE3LjM1OC40MjcuNzY2Ljc5NiAxLjQyOC45MTIgMS43MDkuNzU5IDEuODM5IDEuNzQyIDMuMDY3IDIuNDU5IDMuOTY0LjIyLjI3NS40MzMuNTQxLjU5Ni43NzQtMS4yNjYuMzY1LTMuNTM5IDEuMjA4LTMuMzMyIDUuNDIyLS4xNjcgMi4xMTUtMS4zNTYgMTIuMDE2LTEuOTU5IDE1LjUxNC0uNzk3IDQuNjIxLTIuNDk3IDYuMzQzLTcuMjc5IDcuMzY4em0yMC42OTMtMjMuNjhjLTEuMjk0LjYwMS0zLjQ2IDEuMDUyLTUuNTE4IDEuMTQ4LTIuMjczLjEwNy0zLjQzLS4yNTUtMy43MDItLjQ3Ny0uMTI4LTIuNjI2Ljg1LTIuOTAxIDEuODg0LTMuMTkxLjE2My0uMDQ2LjMyMS0uMDkuNDc0LS4xNDRhNCA0IDAgMCAwIC4zMTMuMjNjMS44MjcgMS4yMDYgNS4wODUgMS4zMzYgOS42ODUuMzg2bC4wNS0uMDFjLS42Mi41OC0xLjY4MiAxLjM1OS0zLjE4NyAyLjA1OHoiLz48L2c+PC9zeW1ib2w+PC9zdmc+"},"displayName":"Postgres","typeVersion":3,"nodeCategories":[{"id":3,"name":"Data & Storage"},{"id":5,"name":"Development"}]},{"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":40,"icon":"file:slack.svg","name":"n8n-nodes-base.slack","codex":{"data":{"alias":["human","form","wait","hitl","approval"],"resources":{"generic":[{"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/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/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/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/automations-for-activists/","icon":"✨","label":"How Common Knowledge use workflow automation for activism"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/slack/"}]},"categories":["Communication","HITL"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"HITL":["Human in the Loop"]}}},"group":"[\"output\"]","defaults":{"name":"Slack"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIgZD0iTTQwLjc0MSA5My41NWMwLTguNzM1IDYuNjA3LTE1Ljc3MiAxNC44MTUtMTUuNzcyczE0LjgxNSA3LjAzNyAxNC44MTUgMTUuNzcydjM4LjgyNGMwIDguNzM3LTYuNjA3IDE1Ljc3NC0xNC44MTUgMTUuNzc0cy0xNC44MTUtNy4wMzctMTQuODE1LTE1Ljc3MnoiLz48cGF0aCBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiIGQ9Ik05My41NSAxMDcuNDA4Yy04LjczNSAwLTE1Ljc3Mi02LjYwNy0xNS43NzItMTQuODE1czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiLz48cGF0aCBmaWxsPSIjMmZiNjdjIiBzdHJva2U9IiMyZmI2N2MiIGQ9Ik03Ny43NzggMTUuNzcyQzc3Ljc3OCA3LjAzNyA4NC4zODUgMCA5Mi41OTMgMHMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjZjMCA4LjczNS02LjYwNyAxNS43NzItMTQuODE1IDE1Ljc3MnMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6Ii8+PHBhdGggZmlsbD0iIzM2YzVmMSIgc3Ryb2tlPSIjMzZjNWYxIiBkPSJNMTUuNzcyIDcwLjM3MUM3LjAzNyA3MC4zNzEgMCA2My43NjMgMCA1NS41NTZzNy4wMzctMTQuODE1IDE1Ljc3Mi0xNC44MTVoMzguODI2YzguNzM1IDAgMTUuNzcyIDYuNjA3IDE1Ljc3MiAxNC44MTVzLTcuMDM3IDE0LjgxNS0xNS43NzIgMTQuODE1eiIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIgZD0iTTc3Ljc3OCAxMzMuMzMzYzAgOC4yMDggNi42MDcgMTQuODE1IDE0LjgxNSAxNC44MTVzMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNS0xNC44MTVINzcuNzc4eiIvPjxwYXRoIGZpbGw9IiMyZmI2N2MiIHN0cm9rZT0iIzJmYjY3YyIgZD0iTTEzMy4zMzQgNzAuMzcxaC0xNC44MTVWNTUuNTU2YzAtOC4yMDcgNi42MDctMTQuODE1IDE0LjgxNS0xNC44MTVzMTQuODE1IDYuNjA3IDE0LjgxNSAxNC44MTUtNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTV6Ii8+PHBhdGggZmlsbD0iI2UwMWU1YSIgc3Ryb2tlPSIjZTAxZTVhIiBkPSJNMTQuODE1IDc3Ljc3OEgyOS42M3YxNC44MTVjMCA4LjIwNy02LjYwNyAxNC44MTUtMTQuODE1IDE0LjgxNVMwIDEwMC44IDAgOTIuNTkzczYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1eiIvPjxwYXRoIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIgZD0iTTcwLjM3MSAxNC44MTVWMjkuNjNINTUuNTU2Yy04LjIwNyAwLTE0LjgxNS02LjYwNy0xNC44MTUtMTQuODE1UzQ3LjM0OCAwIDU1LjU1NiAwczE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1eiIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"},"displayName":"Slack","typeVersion":2,"nodeCategories":[{"id":6,"name":"Communication"},{"id":28,"name":"HITL"}]},{"id":49,"icon":"file:telegram.svg","name":"n8n-nodes-base.telegram","codex":{"data":{"alias":["human","form","wait","hitl","approval"],"resources":{"generic":[{"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/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/world-poetry-day-workflow/","icon":"📜","label":"Celebrating World Poetry Day with a daily poem in Telegram"},{"url":"https://n8n.io/blog/using-automation-to-boost-productivity-in-the-workplace/","icon":"💪","label":"Using Automation to Boost Productivity in the Workplace"},{"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/creating-scheduled-text-affirmations-with-n8n/","icon":"🤟","label":"Creating scheduled text affirmations with n8n"},{"url":"https://n8n.io/blog/creating-telegram-bots-with-n8n-a-no-code-platform/","icon":"💬","label":"Creating Telegram Bots with n8n, a No-Code Platform"},{"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/app-nodes/n8n-nodes-base.telegram/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/telegram/"}]},"categories":["Communication","HITL"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"HITL":["Human in the Loop"]}}},"group":"[\"output\"]","defaults":{"name":"Telegram"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiB2aWV3Qm94PSIwIDAgNjYgNjYiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSI+PHBhdGggZmlsbD0iIzM3YWVlMiIgZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiLz48cGF0aCBmaWxsPSIjYzhkYWVhIiBkPSJtMjEuNjYxIDM0LjMzOCAzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIvPjxwYXRoIGZpbGw9IiNhOWM2ZDgiIGQ9Im0yNi42OTUgMzcuMDM0LS43MjkgNy43NDZzLS4zMDUgMi4zNzMgMi4wNjggMGw0LjY0NC00LjIwMyIvPjxwYXRoIGQ9Im0yMS43MyAzNC43MTItNy44MDktMi41NDVzLS45MzItLjM3OC0uNjMzLTEuMjM3Yy4wNjItLjE3Ny4xODYtLjMyOC41NTktLjU4OCAxLjczMS0xLjIwNiAzMi4wMjgtMTIuMDk2IDMyLjAyOC0xMi4wOTZzLjg1Ni0uMjg4IDEuMzYxLS4wOTdjLjIzMS4wODguMzc4LjE4Ny41MDMuNTQ4LjA0NS4xMzIuMDcxLjQxMS4wNjguNjg5LS4wMDMuMjAxLS4wMjcuMzg2LS4wNDUuNjc4LS4xODQgMi45NzgtNS43MDYgMjUuMTk4LTUuNzA2IDI1LjE5OHMtLjMzIDEuMy0xLjUxNCAxLjM0NWMtLjQzMi4wMTYtLjk1Ni0uMDcxLTEuNTgyLS42MS0yLjMyMy0xLjk5OC0xMC4zNTItNy4zOTQtMTIuMTI2LTguNThhLjM0LjM0IDAgMCAxLS4xNDYtLjIzOWMtLjAyNS0uMTI1LjEwOC0uMjguMTA4LS4yOHMxMy45OC0xMi40MjcgMTQuMzUyLTEzLjczMWMuMDI5LS4xMDEtLjA3OS0uMTUxLS4yMjYtLjEwNy0uOTI5LjM0Mi0xNy4wMjUgMTAuNTA2LTE4LjgwMSAxMS42MjktLjEwNC4wNjYtLjM5NS4wMjMtLjM5NS4wMjMiLz48L2c+PC9zeW1ib2w+PC9zdmc+"},"displayName":"Telegram","typeVersion":1,"nodeCategories":[{"id":6,"name":"Communication"},{"id":28,"name":"HITL"}]},{"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":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":839,"icon":"fa:clock","name":"n8n-nodes-base.scheduleTrigger","codex":{"data":{"alias":["Time","Scheduler","Polling","Cron","Interval"],"resources":{"generic":[],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.scheduletrigger/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"trigger\",\"schedule\"]","defaults":{"name":"Schedule Trigger","color":"#31C49F"},"iconData":{"icon":"clock","type":"icon"},"displayName":"Schedule Trigger","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":1123,"icon":"fa:link","name":"@n8n/n8n-nodes-langchain.chainLlm","codex":{"data":{"alias":["LangChain"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.chainllm/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Chains","Root Nodes"]}}},"group":"[\"transform\"]","defaults":{"name":"Basic LLM Chain","color":"#909298"},"iconData":{"icon":"link","type":"icon"},"displayName":"Basic LLM Chain","typeVersion":2,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]},{"id":1145,"icon":"file:anthropic.svg","name":"@n8n/n8n-nodes-langchain.lmChatAnthropic","codex":{"data":{"alias":["claude","sonnet","opus"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatanthropic/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Language Models","Root Nodes"],"Language Models":["Chat Models (Recommended)"]}}},"group":"[\"transform\"]","defaults":{"name":"Anthropic Chat Model"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NiIgaGVpZ2h0PSIzMiIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzdEN0Q4NyIgZD0iTTMyLjczIDBoLTYuOTQ1TDM4LjQ1IDMyaDYuOTQ1ek0xMi42NjUgMCAwIDMyaDcuMDgybDIuNTktNi43MmgxMy4yNWwyLjU5IDYuNzJoNy4wODJMMTkuOTI5IDB6bS0uNzAyIDE5LjMzNyA0LjMzNC0xMS4yNDYgNC4zMzQgMTEuMjQ2eiIvPjwvc3ZnPg=="},"displayName":"Anthropic Chat Model","typeVersion":1,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]}],"categories":[{"id":16,"name":"DevOps"},{"id":49,"name":"AI Summarization"}],"image":[]}}