This workflow follows the Emailsend → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "KG_Build_Level_1",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"id": "trigger-schedule",
"name": "ScheduleTrigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
250,
300
]
},
{
"parameters": {
"jsCode": "// \u68c0\u67e5\u5f53\u524d\u56fe\u8c31\u5b8c\u6574\u5ea6\nconst { Pool } = require('pg');\nconst neo4j = require('neo4j-driver');\n\nconst pgPool = new Pool({\n host: 'postgres',\n port: 5432,\n database: 'nvda',\n user: 'user',\n password: 'pass'\n});\n\nconst driver = neo4j.driver(\n 'bolt://neo4j:7687',\n neo4j.auth.basic('neo4j', 'nvidia2024kg')\n);\n\nasync function checkCompleteness() {\n const session = driver.session();\n \n try {\n // \u7edf\u8ba1\u5404\u7c7b\u8282\u70b9\n const companyCount = await session.run('MATCH (c:Company) RETURN count(c) as n').then(r => r.records[0].get('n').toNumber());\n const productCount = await session.run('MATCH (p:Product) RETURN count(p) as n').then(r => r.records[0].get('n').toNumber());\n const techCount = await session.run('MATCH (t:Technology) RETURN count(t) as n').then(r => r.records[0].get('n').toNumber());\n const relCount = await session.run('MATCH ()-[r]->() RETURN count(r) as n').then(r => r.records[0].get('n').toNumber());\n \n // \u68c0\u67e5\u7f3a\u5931\u5173\u7cfb\u7684\u516c\u53f8\n const missingSuppliers = await session.run(`\n MATCH (c:Company)\n WHERE NOT (c)-[:SUPPLIES_TO]-() AND c.company_id <> 'NVDA'\n RETURN c.company_id as id, c.name_cn as name\n `).then(r => r.records.map(rec => ({id: rec.get('id'), name: rec.get('name')})));\n \n // \u8ba1\u7b97\u5b8c\u6574\u5ea6\u767e\u5206\u6bd4\n const companyCompleteness = Math.min(companyCount / 30 * 100, 100);\n const productCompleteness = Math.min(productCount / 50 * 100, 100);\n const relationCompleteness = Math.min(relCount / 100 * 100, 100);\n \n return [{\n json: {\n timestamp: new Date().toISOString(),\n completeness: {\n companies: { current: companyCount, target: 30, pct: companyCompleteness },\n products: { current: productCount, target: 50, pct: productCompleteness },\n relations: { current: relCount, target: 100, pct: relationCompleteness },\n overall: (companyCompleteness + productCompleteness + relationCompleteness) / 3\n },\n gaps: {\n missingSuppliers: missingSuppliers.map(c => c.id),\n missingSuppliersCount: missingSuppliers.length\n },\n needsExpansion: companyCount < 30 || productCount < 50 || relCount < 100\n }\n }];\n } finally {\n await session.close();\n }\n}\n\nreturn checkCompleteness();"
},
"id": "check-completeness",
"name": "CheckCompleteness",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
450,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "condition-1",
"leftValue": "={{ $json.needsExpansion }}",
"rightValue": "true",
"operator": {
"type": "boolean",
"operation": "equals"
}
}
]
}
},
"id": "should-expand",
"name": "NeedsExpansion",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
650,
300
]
},
{
"parameters": {
"jsCode": "// \u6839\u636e\u7f3a\u5931\u6570\u636e\u751f\u6210\u4efb\u52a1\u961f\u5217\nconst gaps = $input.first().json.gaps;\nconst completeness = $input.first().json.completeness;\n\nconst tasks = [];\n\n// \u4f18\u5148\u5904\u7406\u7f3a\u5931\u4f9b\u5e94\u5546\u5173\u7cfb\u7684\u516c\u53f8\nif (gaps.missingSuppliersCount > 0) {\n for (const companyId of gaps.missingSuppliers.slice(0, 3)) {\n tasks.push({\n taskType: 'enrich_company_relations',\n priority: 'high',\n targetCompany: companyId,\n description: `\u67e5\u627e ${companyId} \u7684\u4f9b\u5e94\u5173\u7cfb`\n });\n }\n}\n\n// \u5982\u679c\u516c\u53f8\u6570\u91cf\u4e0d\u8db3\uff0c\u6dfb\u52a0\u65b0\u516c\u53f8\nif (completeness.companies.current < 30) {\n tasks.push({\n taskType: 'discover_companies',\n priority: 'medium',\n sector: 'upstream_materials',\n description: '\u53d1\u73b0\u4e0a\u6e38\u6750\u6599\u516c\u53f8'\n });\n}\n\n// \u5982\u679c\u4ea7\u54c1\u6570\u91cf\u4e0d\u8db3\nif (completeness.products.current < 50) {\n tasks.push({\n taskType: 'discover_products',\n priority: 'medium',\n company: 'NVDA',\n description: '\u53d1\u73b0NVIDIA\u65b0\u4ea7\u54c1'\n });\n}\n\nreturn tasks.map(t => ({ json: t }));"
},
"id": "generate-tasks",
"name": "GenerateTasks",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
200
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.DATA_SERVICE_URL }}/api/kg/enrich",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "taskType",
"value": "={{ $json.taskType }}"
},
{
"name": "targetCompany",
"value": "={{ $json.targetCompany }}"
},
{
"name": "priority",
"value": "={{ $json.priority }}"
}
]
},
"options": {}
},
"id": "enrich-kg",
"name": "EnrichKnowledgeGraph",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1050,
200
]
},
{
"parameters": {
"jsCode": "// \u9a8c\u8bc1\u65b0\u6dfb\u52a0\u7684\u6570\u636e\nconst neo4j = require('neo4j-driver');\nconst driver = neo4j.driver('bolt://neo4j:7687', neo4j.auth.basic('neo4j', 'nvidia2024kg'));\n\nasync function validate() {\n const session = driver.session();\n try {\n // \u68c0\u67e5\u65b0\u6dfb\u52a0\u7684\u5173\u7cfb\u662f\u5426\u6709\u5b8c\u6574\u7684\u5c5e\u6027\n const result = await session.run(`\n MATCH ()-[r:SUPPLIES_TO]->()\n WHERE r.dependency_score IS NULL OR r.substitutability IS NULL\n RETURN count(r) as incomplete\n `);\n \n const incomplete = result.records[0].get('incomplete').toNumber();\n \n return [{\n json: {\n validated: incomplete === 0,\n incompleteRelations: incomplete,\n message: incomplete > 0 ? `${incomplete} \u6761\u5173\u7cfb\u7f3a\u5c11\u5b8c\u6574\u5c5e\u6027` : '\u6240\u6709\u5173\u7cfb\u5df2\u5b8c\u6574\u6807\u6ce8'\n }\n }];\n } finally {\n await session.close();\n }\n}\n\nreturn validate();"
},
"id": "validate-data",
"name": "ValidateNewData",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1250,
200
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.DATA_SERVICE_URL }}/webhook/kg-progress",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "event",
"value": "kg_build_iteration_complete"
},
{
"name": "completeness",
"value": "={{ $json }}"
}
]
}
},
"id": "notify-progress",
"name": "NotifyProgress",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1450,
200
]
},
{
"parameters": {
"jsCode": "// \u68c0\u67e5\u662f\u5426\u8fbe\u5230 Level 1 \u5b8c\u6574\u5ea6\nconst completeness = $input.first().json.completeness;\n\nconst level1Complete = \n completeness.companies.current >= 30 &&\n completeness.products.current >= 50 &&\n completeness.relations.current >= 100;\n\nreturn [{\n json: {\n level1Complete,\n message: level1Complete ? '\u2705 Level 1 \u77e5\u8bc6\u56fe\u8c31\u6784\u5efa\u5b8c\u6210' : '\u23f3 \u7ee7\u7eed\u6784\u5efa\u4e2d...',\n nextCheck: level1Complete ? null : '6\u5c0f\u65f6\u540e',\n stats: completeness\n }\n}];"
},
"id": "check-level1",
"name": "CheckLevel1Complete",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
400
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "condition-1",
"leftValue": "={{ $json.level1Complete }}",
"rightValue": "true",
"operator": {
"type": "boolean",
"operation": "equals"
}
}
]
}
},
"id": "is-complete",
"name": "IsLevel1Complete",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1050,
400
]
},
{
"parameters": {
"to": "{{ $env.ALERT_EMAIL }}",
"subject": "\ud83c\udfaf NVIDIA KG Level 1 \u6784\u5efa\u5b8c\u6210",
"text": "={{ $json.message }}\\n\\n\u7edf\u8ba1:\\n- \u516c\u53f8: {{ $json.stats.companies.current }}/{{ $json.stats.companies.target }}\\n- \u4ea7\u54c1: {{ $json.stats.products.current }}/{{ $json.stats.products.target }}\\n- \u5173\u7cfb: {{ $json.stats.relations.current }}/{{ $json.stats.relations.target }}\\n\\n\u6574\u4f53\u5b8c\u6574\u5ea6: {{ $json.stats.overall.toFixed(1) }}%"
},
"id": "send-completion-email",
"name": "SendCompletionEmail",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [
1250,
300
]
},
{
"parameters": {
"to": "{{ $env.ALERT_EMAIL }}",
"subject": "\ud83d\udcca NVIDIA KG \u6784\u5efa\u8fdb\u5ea6\u62a5\u544a",
"text": "={{ $json.message }}\\n\\n\u5f53\u524d\u8fdb\u5ea6:\\n- \u516c\u53f8: {{ $json.stats.companies.current }}/{{ $json.stats.companies.target }} ({{ $json.stats.companies.pct.toFixed(1) }}%)\\n- \u4ea7\u54c1: {{ $json.stats.products.current }}/{{ $json.stats.products.target }} ({{ $json.stats.products.pct.toFixed(1) }}%)\\n- \u5173\u7cfb: {{ $json.stats.relations.current }}/{{ $json.stats.relations.target }} ({{ $json.stats.relations.pct.toFixed(1) }}%)\\n\\n\u4e0b\u6b21\u68c0\u67e5: {{ $json.nextCheck }}"
},
"id": "send-progress-email",
"name": "SendProgressEmail",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [
1250,
500
]
}
],
"connections": {
"ScheduleTrigger": {
"main": [
[
{
"node": "CheckCompleteness",
"type": "main",
"index": 0
}
]
]
},
"CheckCompleteness": {
"main": [
[
{
"node": "NeedsExpansion",
"type": "main",
"index": 0
}
]
]
},
"NeedsExpansion": {
"main": [
[
{
"node": "GenerateTasks",
"type": "main",
"index": 0
}
],
[
{
"node": "CheckLevel1Complete",
"type": "main",
"index": 0
}
]
]
},
"GenerateTasks": {
"main": [
[
{
"node": "EnrichKnowledgeGraph",
"type": "main",
"index": 0
}
]
]
},
"EnrichKnowledgeGraph": {
"main": [
[
{
"node": "ValidateNewData",
"type": "main",
"index": 0
}
]
]
},
"ValidateNewData": {
"main": [
[
{
"node": "NotifyProgress",
"type": "main",
"index": 0
}
]
]
},
"CheckLevel1Complete": {
"main": [
[
{
"node": "IsLevel1Complete",
"type": "main",
"index": 0
}
]
]
},
"IsLevel1Complete": {
"main": [
[
{
"node": "SendCompletionEmail",
"type": "main",
"index": 0
}
],
[
{
"node": "SendProgressEmail",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all",
"saveExecutionProgress": true,
"saveManualExecutions": true,
"timezone": "Asia/Shanghai"
},
"staticData": null,
"tags": [
"knowledge-graph",
"p0",
"auto-build"
]
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
KG_Build_Level_1. Uses httpRequest, emailSend. Scheduled trigger; 11 nodes.
Source: https://github.com/123qsa/deer-flow/blob/main/nvidia-supply-chain/n8n/workflows/wf00_kg_build_level1.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Birthday Automation - Production (Fixed). Uses stopAndError, httpRequest, emailSend, bannerbear. Scheduled trigger; 86 nodes.
This workflow is an improvement of this workflow by Greg Brzezinka.
N8N-Self-Updater. Uses ssh, emailSend, httpRequest. Scheduled trigger; 27 nodes.
> An automated n8n workflow originally built for DigitalOcean-based n8n deployments, but fully compatible with any VPS or cloud hosting (e.g., AWS, Google Cloud, Hetzner, Linode, etc.) where n8n ru
What if you could spot a major sales problem—or a winning campaign—the very next morning, instead of weeks later? Imagine receiving a beautiful, data-rich alert directly in your inbox the moment your