Phase 3: add onboarding workflow
Webhook /webhook/onboard → read Clients → compute next C-#### → append Clients row (status onboarding, renewal_date auto from billing cycle) → seed Projects row (checklist from services, +14d go-live) → log Activity → Telegram confirmation. Sheets writes retry on fail. Verified end-to-end (C-0002 created across all three tabs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,366 @@
|
|||||||
|
{
|
||||||
|
"name": "SMB \u00b7 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": {
|
||||||
|
"authentication": "serviceAccount",
|
||||||
|
"resource": "sheet",
|
||||||
|
"operation": "read",
|
||||||
|
"documentId": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "1raMSWRZw_JfHlWqOb3LbhaQ6LWx0VGblxIV4Z2pSzp8",
|
||||||
|
"mode": "id"
|
||||||
|
},
|
||||||
|
"sheetName": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "470934735",
|
||||||
|
"mode": "list",
|
||||||
|
"cachedResultName": "Clients"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "92d09e48-6cac-43fc-b203-5dde0d08f61a",
|
||||||
|
"name": "Read Clients",
|
||||||
|
"type": "n8n-nodes-base.googleSheets",
|
||||||
|
"typeVersion": 4.5,
|
||||||
|
"position": [
|
||||||
|
460,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"googleApi": {
|
||||||
|
"id": "45TWPyl1wVk2Vxdm",
|
||||||
|
"name": "SMB Google (service account)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "\nconst w=$('Onboard webhook').first().json; const body=w.body||w;\nconst rows=$('Read Clients').all().map(i=>i.json);\nlet maxC=0;\nfor(const r of rows){const m=String(r.client_id||'').match(/C-(\\d+)/);if(m)maxC=Math.max(maxC,+m[1]);}\nconst cid='C-'+String(maxC+1).padStart(4,'0');\nconst s=v=>{v=(v==null?'':String(v));return /^[=+\\-@\\t\\r]/.test(v)?\"'\"+v:v;};\nconst now=new Date();\nconst nowStr=now.toISOString().replace('T',' ').slice(0,16);\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);\nlet renewal='';\nif(cycle==='monthly'){const d=new Date(start);d.setUTCMonth(d.getUTCMonth()+1);renewal=d.toISOString().slice(0,10);}\nelse if(cycle==='yearly'){const d=new Date(start);d.setUTCFullYear(d.getUTCFullYear()+1);renewal=d.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={client_id:cid,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,renewal_date:renewal,created_at:nowStr,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 projects=[{project_id:'P-'+Date.now(),client_id:cid,deliverable:'Onboarding & Setup',\n tier,checklist,go_live_date:goLive,status:'todo'}];\nconst activity={ts:nowStr,workflow:'onboarding',client_id:cid,action:'client onboarded',\n detail:(client.business_name||'')+' / Tier '+tier+' / '+services,result:'ok'};\nconst summary='\u2705 Neuer Kunde angelegt: '+cid+'\\n'+(client.business_name||'')+' \u2014 Tier '+tier+\n '\\nServices: '+services+'\\nGeb\u00fchr: '+(client.monthly_fee_eur||'\u2014')+'\u20ac/'+cycle+\n '\\nVerl\u00e4ngerung: '+(renewal||'\u2014')+'\\nGo-Live Ziel: '+goLive;\nreturn [{json:{client,projects,activity,summary,cid}}];\n"
|
||||||
|
},
|
||||||
|
"id": "fe9a0f5d-c6f8-4328-a98e-edc022582ec3",
|
||||||
|
"name": "Compute IDs",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
680,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{json: $('Compute IDs').first().json.client}];"
|
||||||
|
},
|
||||||
|
"id": "4abc9b06-83ec-4d90-bb53-020e2dfade0d",
|
||||||
|
"name": "Map client",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
900,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"authentication": "serviceAccount",
|
||||||
|
"resource": "sheet",
|
||||||
|
"operation": "append",
|
||||||
|
"documentId": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "1raMSWRZw_JfHlWqOb3LbhaQ6LWx0VGblxIV4Z2pSzp8",
|
||||||
|
"mode": "id"
|
||||||
|
},
|
||||||
|
"sheetName": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "470934735",
|
||||||
|
"mode": "list",
|
||||||
|
"cachedResultName": "Clients"
|
||||||
|
},
|
||||||
|
"options": {},
|
||||||
|
"columns": {
|
||||||
|
"mappingMode": "autoMapInputData",
|
||||||
|
"value": {},
|
||||||
|
"matchingColumns": [],
|
||||||
|
"schema": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "330f5cec-c1dd-4197-92bf-01c66f9a795a",
|
||||||
|
"name": "Append Clients",
|
||||||
|
"type": "n8n-nodes-base.googleSheets",
|
||||||
|
"typeVersion": 4.5,
|
||||||
|
"position": [
|
||||||
|
1120,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"googleApi": {
|
||||||
|
"id": "45TWPyl1wVk2Vxdm",
|
||||||
|
"name": "SMB Google (service account)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retryOnFail": true,
|
||||||
|
"maxTries": 4,
|
||||||
|
"waitBetweenTries": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return $('Compute IDs').first().json.projects.map(p=>({json:p}));"
|
||||||
|
},
|
||||||
|
"id": "cc50083b-44f6-450b-ada3-ad3f2142441b",
|
||||||
|
"name": "Map projects",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1340,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"authentication": "serviceAccount",
|
||||||
|
"resource": "sheet",
|
||||||
|
"operation": "append",
|
||||||
|
"documentId": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "1raMSWRZw_JfHlWqOb3LbhaQ6LWx0VGblxIV4Z2pSzp8",
|
||||||
|
"mode": "id"
|
||||||
|
},
|
||||||
|
"sheetName": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "619112786",
|
||||||
|
"mode": "list",
|
||||||
|
"cachedResultName": "Projects"
|
||||||
|
},
|
||||||
|
"options": {},
|
||||||
|
"columns": {
|
||||||
|
"mappingMode": "autoMapInputData",
|
||||||
|
"value": {},
|
||||||
|
"matchingColumns": [],
|
||||||
|
"schema": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "83b26e24-edd8-4663-b61c-aa5e25b33842",
|
||||||
|
"name": "Append Projects",
|
||||||
|
"type": "n8n-nodes-base.googleSheets",
|
||||||
|
"typeVersion": 4.5,
|
||||||
|
"position": [
|
||||||
|
1560,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"googleApi": {
|
||||||
|
"id": "45TWPyl1wVk2Vxdm",
|
||||||
|
"name": "SMB Google (service account)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retryOnFail": true,
|
||||||
|
"maxTries": 4,
|
||||||
|
"waitBetweenTries": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{json: $('Compute IDs').first().json.activity}];"
|
||||||
|
},
|
||||||
|
"id": "8d3c0640-e186-4813-815e-8f807d7b6f4f",
|
||||||
|
"name": "Map activity",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1780,
|
||||||
|
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"
|
||||||
|
},
|
||||||
|
"options": {},
|
||||||
|
"columns": {
|
||||||
|
"mappingMode": "autoMapInputData",
|
||||||
|
"value": {},
|
||||||
|
"matchingColumns": [],
|
||||||
|
"schema": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "c8773711-99d4-4722-9a13-5c8c9afa7060",
|
||||||
|
"name": "Append Activity",
|
||||||
|
"type": "n8n-nodes-base.googleSheets",
|
||||||
|
"typeVersion": 4.5,
|
||||||
|
"position": [
|
||||||
|
1780,
|
||||||
|
460
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"googleApi": {
|
||||||
|
"id": "45TWPyl1wVk2Vxdm",
|
||||||
|
"name": "SMB Google (service account)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retryOnFail": true,
|
||||||
|
"maxTries": 4,
|
||||||
|
"waitBetweenTries": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "sendMessage",
|
||||||
|
"chatId": "5499280257",
|
||||||
|
"text": "={{ $('Compute IDs').first().json.summary }}",
|
||||||
|
"additionalFields": {
|
||||||
|
"appendAttribution": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "ae19e543-1a6b-4466-8af8-bb436f51091b",
|
||||||
|
"name": "Notify Telegram",
|
||||||
|
"type": "n8n-nodes-base.telegram",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"position": [
|
||||||
|
2000,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"telegramApi": {
|
||||||
|
"id": "d9s4Ec5ocEH8FAxh",
|
||||||
|
"name": "SMB Telegram (leads bot)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Onboard webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Read Clients",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Read Clients": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Compute IDs",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Compute IDs": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Map client",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Map client": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Append Clients",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Append Clients": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Map projects",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Map projects": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Append Projects",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Append Projects": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Map activity",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Map activity": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Append Activity",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Append Activity": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Notify Telegram",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1",
|
||||||
|
"callerPolicy": "workflowsFromSameOwner",
|
||||||
|
"availableInMCP": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user