d5a304ebe9
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>
113 lines
5.4 KiB
JSON
113 lines
5.4 KiB
JSON
{
|
|
"name": "SMB · Onboarding",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "onboard",
|
|
"responseMode": "onReceived",
|
|
"options": { "allowedOrigins": "*" }
|
|
},
|
|
"id": "d1a08bba-5cc5-48fa-8063-bda2d58cab15",
|
|
"name": "Onboard webhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2,
|
|
"position": [240, 300],
|
|
"webhookId": "96c28466-ee4b-4aa5-8f06-52d098b497a7"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const w=$('Onboard webhook').first().json; const body=w.body||w;\nconst s=v=>{v=(v==null?'':String(v));return /^[=+\\-@\\t\\r]/.test(v)?\"'\"+v:v;};\nconst now=new Date();\nfunction pd(v){if(!v)return null;const m=String(v).match(/^(\\d{4})-(\\d{2})-(\\d{2})/);\n if(m)return new Date(Date.UTC(+m[1],+m[2]-1,+m[3]));const d=new Date(v);return isNaN(d)?null:d;}\nconst cycle=String(body.billing_cycle||'monthly').toLowerCase();\nconst start=pd(body.start_date)||new Date(Date.UTC(now.getUTCFullYear(),now.getUTCMonth(),now.getUTCDate()));\nconst startStr=start.toISOString().slice(0,10);\nconst tier=String(body.tier||'A').toUpperCase().includes('B')?'B':'A';\nconst services=String(body.services||'site, booking, leads');\nconst client={business_name:s(body.business_name),owner_name:s(body.owner_name),\n email:s(body.email),phone:s(body.phone),niche:s(body.niche),tier,status:'onboarding',\n domain:s(body.domain),stack_notes:s(body.stack_notes),vault_ref:s(body.vault_ref),\n services:s(services),billing_cycle:cycle,monthly_fee_eur:(body.monthly_fee_eur||''),\n start_date:startStr,notes:s(body.notes)};\nconst checklist=services.split(',').map(x=>x.trim()).filter(Boolean).map(x=>x+'\\u2610').join(' ');\nconst gl=new Date(start);gl.setUTCDate(gl.getUTCDate()+14);const goLive=gl.toISOString().slice(0,10);\nconst project={project_id:'P-'+Date.now(),deliverable:'Onboarding & Setup',tier,checklist,\n go_live_date:goLive,status:'todo'};\nreturn [{json:{client,project,tier,services,cycle,monthly_fee_eur:(body.monthly_fee_eur||''),\n business_name:client.business_name}}];"
|
|
},
|
|
"id": "fe9a0f5d-c6f8-4328-a98e-edc022582ec3",
|
|
"name": "Compute",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [460, 300]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://smb-crm:8080/api/clients",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [ { "name": "X-CRM-Token", "value": "__CRM_TOKEN__" } ]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ JSON.stringify($json.client) }}",
|
|
"options": {}
|
|
},
|
|
"id": "22222222-3333-4444-5555-666666666666",
|
|
"name": "Save client",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [680, 300],
|
|
"retryOnFail": true,
|
|
"maxTries": 4,
|
|
"waitBetweenTries": 3000
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const c=$('Compute').first().json;\nconst cid=$('Save client').first().json.added;\nconst p=Object.assign({}, c.project, {client_id:cid});\nreturn [{json:p}];"
|
|
},
|
|
"id": "33333333-4444-5555-6666-777777777777",
|
|
"name": "Build project",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [900, 300]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://smb-crm:8080/api/projects",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [ { "name": "X-CRM-Token", "value": "__CRM_TOKEN__" } ]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ JSON.stringify($json) }}",
|
|
"options": {}
|
|
},
|
|
"id": "44444444-5555-6666-7777-888888888888",
|
|
"name": "Save project",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [1120, 300],
|
|
"retryOnFail": true,
|
|
"maxTries": 4,
|
|
"waitBetweenTries": 3000
|
|
},
|
|
{
|
|
"parameters": {
|
|
"resource": "message",
|
|
"operation": "sendMessage",
|
|
"chatId": "5499280257",
|
|
"text": "=✅ Neuer Kunde angelegt: {{ $('Save client').item.json.added }}\n{{ $('Compute').item.json.business_name }} — Tier {{ $('Compute').item.json.tier }}\nServices: {{ $('Compute').item.json.services }}\nGebühr: {{ $('Compute').item.json.monthly_fee_eur || '—' }}€/{{ $('Compute').item.json.cycle }}\nGo-Live Ziel: {{ $('Build project').item.json.go_live_date }}",
|
|
"additionalFields": { "appendAttribution": false }
|
|
},
|
|
"id": "ae19e543-1a6b-4466-8af8-bb436f51091b",
|
|
"name": "Notify Telegram",
|
|
"type": "n8n-nodes-base.telegram",
|
|
"typeVersion": 1.2,
|
|
"position": [1340, 300],
|
|
"credentials": {
|
|
"telegramApi": { "id": "d9s4Ec5ocEH8FAxh", "name": "SMB Telegram (leads bot)" }
|
|
}
|
|
}
|
|
],
|
|
"connections": {
|
|
"Onboard webhook": { "main": [[{ "node": "Compute", "type": "main", "index": 0 }]] },
|
|
"Compute": { "main": [[{ "node": "Save client", "type": "main", "index": 0 }]] },
|
|
"Save client": { "main": [[{ "node": "Build project", "type": "main", "index": 0 }]] },
|
|
"Build project": { "main": [[{ "node": "Save project", "type": "main", "index": 0 }]] },
|
|
"Save project": { "main": [[{ "node": "Notify Telegram", "type": "main", "index": 0 }]] }
|
|
},
|
|
"settings": {
|
|
"executionOrder": "v1",
|
|
"callerPolicy": "workflowsFromSameOwner",
|
|
"availableInMCP": false
|
|
}
|
|
}
|