n8n ingest swap: lead-intake & onboarding write to the DB

Both flows now POST into the smb-crm DB API instead of appending to Sheets, so
Postgres stays the source of truth and the DB->Sheets mirror keeps the Sheet
current. Telegram notifications unchanged.

- lead-intake: Webhook -> Build lead row -> POST /api/leads -> Telegram
  (dropped the direct Sheets append + the redundant activity-log nodes; the DB
  service logs + mirrors activity itself).
- onboarding: Webhook -> Compute -> POST /api/clients (DB assigns C-id) ->
  Build project w/ returned id -> POST /api/projects -> Telegram. DB computes
  renewal_date; go-live = start + 14d.
- Token injected at deploy time; repo keeps the __CRM_TOKEN__ placeholder.

Verified end-to-end on the live webhooks: lead and client+project land in the
DB and mirror to Sheets; test data cleaned; DB/Sheets consistent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 09:33:53 +02:00
parent e59996a385
commit d5a304ebe9
3 changed files with 94 additions and 469 deletions
+26 -154
View File
@@ -1,5 +1,5 @@
{
"name": "SMB \u00b7 Lead Intake",
"name": "SMB · Lead Intake",
"nodes": [
{
"parameters": {
@@ -14,63 +14,39 @@
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
300
],
"position": [240, 300],
"webhookId": "bb5f87d2-4647-4f38-9d6b-82a863158937"
},
{
"parameters": {
"jsCode": "const i=$input.first().json; const b=i.body||i;\nconst now=new Date().toISOString().replace('T',' ').slice(0,16);\nconst s=v=>{v=(v==null?'':String(v)); return /^[=+\\-@\\t\\r]/.test(v)?\"'\"+v:v;};\nreturn [{ json:{ lead_id:'L-'+Date.now(), received_at:now, client_id:s(b.client_id),\n source:s(b.source), name:s(b.name), contact:s(b.contact||b.email||b.phone),\n service_interest:s(b.service_interest||b.service), message:s(b.message), status:'new',\n notified:'TRUE' }}];"
"jsCode": "const i=$input.first().json; const b=i.body||i;\nconst now=new Date().toISOString().replace('T',' ').slice(0,16);\nconst s=v=>{v=(v==null?'':String(v)); return /^[=+\\-@\\t\\r]/.test(v)?\"'\"+v:v;};\nreturn [{ json:{ lead_id:'L-'+Date.now(), received_at:now, client_id:s(b.client_id),\n source:s(b.source), name:s(b.name), contact:s(b.contact||b.email||b.phone),\n service_interest:s(b.service_interest||b.service), message:s(b.message), status:'new',\n notified:true }}];"
},
"id": "a5d0db99-93c2-46aa-8c0f-af1f4ee7300c",
"name": "Build lead row",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
]
"position": [460, 300]
},
{
"parameters": {
"authentication": "serviceAccount",
"resource": "sheet",
"operation": "append",
"documentId": {
"__rl": true,
"value": "1raMSWRZw_JfHlWqOb3LbhaQ6LWx0VGblxIV4Z2pSzp8",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "571719114",
"mode": "list",
"cachedResultName": "Leads"
},
"columns": {
"mappingMode": "autoMapInputData",
"value": {},
"matchingColumns": [],
"schema": []
"method": "POST",
"url": "http://smb-crm:8080/api/leads",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{ "name": "X-CRM-Token", "value": "__CRM_TOKEN__" }
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"id": "04c41d84-fb8c-41e3-aaba-5db270096a0c",
"name": "Append to Leads",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
680,
300
],
"credentials": {
"googleApi": {
"id": "45TWPyl1wVk2Vxdm",
"name": "SMB Google (service account)"
}
},
"id": "11111111-2222-3333-4444-555555555555",
"name": "Save to CRM (DB)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [680, 300],
"retryOnFail": true,
"maxTries": 4,
"waitBetweenTries": 3000
@@ -80,7 +56,7 @@
"resource": "message",
"operation": "sendMessage",
"chatId": "5499280257",
"text": "=\ud83c\udd95 Neue Anfrage\nName: {{ $('Build lead row').item.json.name || '\u2014' }}\nKontakt: {{ $('Build lead row').item.json.contact || '\u2014' }}\nInteresse: {{ $('Build lead row').item.json.service_interest || '\u2014' }}\nQuelle: {{ $('Build lead row').item.json.source || '\u2014' }}\nClient: {{ $('Build lead row').item.json.client_id || '\u2014' }}\nNachricht: {{ $('Build lead row').item.json.message || '\u2014' }}",
"text": "=🆕 Neue Anfrage\nName: {{ $('Build lead row').item.json.name || '' }}\nKontakt: {{ $('Build lead row').item.json.contact || '' }}\nInteresse: {{ $('Build lead row').item.json.service_interest || '' }}\nQuelle: {{ $('Build lead row').item.json.source || '' }}\nClient: {{ $('Build lead row').item.json.client_id || '' }}\nNachricht: {{ $('Build lead row').item.json.message || '' }}",
"additionalFields": {
"appendAttribution": false
}
@@ -89,128 +65,24 @@
"name": "Notify Telegram",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
900,
300
],
"position": [900, 300],
"credentials": {
"telegramApi": {
"id": "d9s4Ec5ocEH8FAxh",
"name": "SMB Telegram (leads bot)"
}
}
},
{
"parameters": {
"jsCode": "const lead=$('Build lead row').first().json;\nconst now=new Date().toISOString().replace('T',' ').slice(0,16);\nreturn [{ json:{ ts:now, workflow:'lead-intake', client_id:lead.client_id||'',\n action:'lead captured + notified', detail:(lead.name||'')+' / '+(lead.contact||''), result:'ok' }}];"
},
"id": "0c8f8de9-565a-40f3-a24f-cd1833545c4d",
"name": "Build activity",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
300
]
},
{
"parameters": {
"authentication": "serviceAccount",
"resource": "sheet",
"operation": "append",
"documentId": {
"__rl": true,
"value": "1raMSWRZw_JfHlWqOb3LbhaQ6LWx0VGblxIV4Z2pSzp8",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "170940481",
"mode": "list",
"cachedResultName": "Activity Log"
},
"columns": {
"mappingMode": "autoMapInputData",
"value": {},
"matchingColumns": [],
"schema": []
},
"options": {}
},
"id": "6b4bf9d8-e850-473e-85d1-45aaa709d06a",
"name": "Append to Activity Log",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
1340,
300
],
"credentials": {
"googleApi": {
"id": "45TWPyl1wVk2Vxdm",
"name": "SMB Google (service account)"
}
},
"retryOnFail": true,
"maxTries": 4,
"waitBetweenTries": 3000
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Build lead row",
"type": "main",
"index": 0
}
]
]
"main": [[{ "node": "Build lead row", "type": "main", "index": 0 }]]
},
"Build lead row": {
"main": [
[
{
"node": "Append to Leads",
"type": "main",
"index": 0
}
]
]
"main": [[{ "node": "Save to CRM (DB)", "type": "main", "index": 0 }]]
},
"Append to Leads": {
"main": [
[
{
"node": "Notify Telegram",
"type": "main",
"index": 0
}
]
]
},
"Notify Telegram": {
"main": [
[
{
"node": "Build activity",
"type": "main",
"index": 0
}
]
]
},
"Build activity": {
"main": [
[
{
"node": "Append to Activity Log",
"type": "main",
"index": 0
}
]
]
"Save to CRM (DB)": {
"main": [[{ "node": "Notify Telegram", "type": "main", "index": 0 }]]
}
},
"settings": {
@@ -218,4 +90,4 @@
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
}
}
}