Synaplan
開源 AI 平台,供企業使用:自行託管的 AI 代理、符合 GDPR 的聊天機器人小工具、RAG 知識庫、WhatsApp 與 Outlook AI、MCP、多模型路由。Docker、Kubernetes 或託管於 Germ
AI 翻譯
尚未收集儲存庫資訊。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
n8n workflow for customer health risk detection, task creation, Slack alerts, and API error handling.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗An n8n workflow that monitors customer health data, detects at-risk customers, creates follow-up tasks, sends Slack alerts, and logs API failures.
This workflow reads customer data from Google Sheets and evaluates multiple risk indicators:
When a customer is considered at risk, the workflow creates a follow-up task and checks whether that task already exists before adding it to the tracking sheet.
For newly created high-priority tasks, the workflow sends a notification to Slack.
Schedule Trigger
↓
Read Customers from Google Sheets
↓
Calculate Customer Metrics
↓
Evaluate Risk Conditions
↓
Is Customer At Risk?
↓ Yes
Build Task Payload
↓
Generate Task ID
↓
Check Existing Tasks
↓
Task Already Exists?
↓ No
Append Task to Google Sheets
↓
Send Slack Alert
↓ Error
Prepare Slack Error Log
↓
Log Slack Error
This repository ships a small Docker Compose setup to run n8n locally on Windows
with Docker Desktop. The instance is bound to 127.0.0.1 only and is never
exposed on the network or the Internet.
docker --version and docker compose version must both answer.envCopy-Item .env.example .env
Then open .env and set N8N_ENCRYPTION_KEY to a long random value, for example:
[Convert]::ToBase64String((1..48 | ForEach-Object { Get-Random -Max 256 }))
.env is git-ignored and must never be committed. Keep the encryption key
safe: if you lose it, every credential stored in n8n becomes unreadable.
.\scripts\start-n8n.ps1
# or directly:
docker compose up -d
The editor is then available at http://localhost:5678.
On the very first start, create your local owner account in that interface.
.\scripts\stop-n8n.ps1
# or directly:
docker compose down
docker compose down stops the container but keeps your data.
Never run docker compose down -v unless you really want to wipe the
instance: -v deletes the persistent volume.
.\scripts\logs-n8n.ps1 # last 100 lines
.\scripts\logs-n8n.ps1 -Follow # live tail
.\scripts\status-n8n.ps1 # container + volume state
All persistent state is stored in the Docker named volume n8n_data, mounted at
/home/node/.n8n inside the container. It holds:
This project uses SQLite (the n8n default); no PostgreSQL is required.
The ./workflow folder is mounted read-only at /workflow inside the container.
Import it after creating your owner account:
.\scripts\import-workflow.ps1
# or directly:
docker compose exec n8n n8n import:workflow --input=/workflow/customer-health-risk-detection.json
The workflow is shipped with "active": false, so it will not run on import.
The JSON contains no credentials. You must configure the following by hand in the n8n interface:
YOUR_GOOGLE_SHEET_ID with your real spreadsheet IDYOUR_SLACK_CHANNEL_ID with your real Slack channel ID.envThe responsive Next.js application makes the workflow usable by a Customer Success Manager without opening n8n.
Live application: https://n8n-customer-health-risk-detection.vercel.app/
Status verified on 24 October 2026: the public URL loads, but it still serves an earlier UI and
/api/analyzereports thatN8N_WEBHOOK_URLis not configured. Complete the deployment checklist before Demo Day.
Customer-success teams often inspect usage, NPS, support escalations, and renewal dates manually. That makes detection slow and inconsistent. This prototype gives Customer Success Managers and CS Operations teams one repeatable, evidence-backed assessment and one concrete next action.
client_id to /api/analyze.The interface never presents a mocked response as a live analysis. Missing configuration and upstream failures are shown as visible error states.
Clients, Tasks, and Error Logsflowchart LR
U[Customer Success user] --> UI[Next.js web application]
UI --> API[POST /api/analyze]
API --> WH[n8n Webhook\ncustomer-risk-analysis]
WH --> GS[(Google Sheets\nClients)]
GS --> RULES[Deterministic metrics\nand risk flags]
RULES --> CLASSIFIER[Risk Classifier Agent]
RULES --> EXTRACTOR[Signal Extractor Agent]
CLASSIFIER --> MERGE[Merge agent analysis]
EXTRACTOR --> MERGE
MERGE --> REASONER[Action Reasoner Agent]
REASONER --> RESPONSE[Structured JSON response]
RESPONSE --> API
API --> UI
REASONER -. optional task path .-> TASKS[(Google Sheets\nTasks)]
TASKS -. optional notification .-> SLACK[Slack]
SLACK -. failure log .-> ERRORS[(Google Sheets\nError Logs)]
Structured Output Parser nodes constrain each agent response.
frontend/ Next.js application and API route
workflow/ n8n import templates
sample-data/ Safe example customer data
scripts/ n8n lifecycle and sanitization helpers
docs/ Screenshots and M5 documentation
compose.yaml Local n8n service
Set-Location frontend
Copy-Item .env.example .env.local
npm ci
npm run dev
Open http://localhost:3000.
Import workflow/customer-health-risk-detection-web-app.json. This sanitized
37-node template is inactive and contains no credential or real resource ID.
Configure:
YOUR_GOOGLE_SHEET_ID;Clients, Tasks, and Error Logs worksheets;YOUR_SLACK_CHANNEL_ID only after reconnecting and
testing the optional notification path.In the verified 37-node export, the Webhook analysis path returns the frontend response, while the retained Tasks/Slack branch is not connected to that path. Do not present Slack as part of a Web analysis until that branch is deliberately rewired and tested in a duplicate workflow.
Publish only after testing. The production endpoint must end with
/webhook/customer-risk-analysis.
frontend/.env.local and Vercel require two server-only variables:
N8N_WEBHOOK_URL=https://n8n.example.com/webhook/customer-risk-analysis
N8N_WEBHOOK_SECRET=<same value as the n8n Header Auth credential>
A Vercel deployment cannot reach localhost. Do not prefix either variable
with NEXT_PUBLIC_. See the Railway deployment guide
for the prepared n8n hosting configuration.
Request:
{ "client_id": "C005" }
Successful response:
{
"success": true,
"customer": {
"id": "C005",
"name": "Ember Agency",
"riskLevel": "HIGH",
"riskCategory": "ENGAGEMENT_RISK",
"riskSignals": ["no login for 62 days"],
"priority": "HIGH",
"recommendedAction": "Contact the customer about the drop in usage.",
"taskStatus": "RISK_DETECTED"
}
}
The API rejects missing or unknown demo IDs, handles timeouts and non-JSON responses, and validates the complete n8n response before returning it.
Set-Location frontend
npm run lint
npm test
npm run build
Automated tests are described in Testing & Production Hardening (M6).
Manual checks:
/analyze with no selection and verify the visible error state.frontend.N8N_WEBHOOK_URL and N8N_WEBHOOK_SECRET to Preview and Production.During M5, the application failed in production in several ways: expired
Google OAuth credentials, HTTP 500/502/504 responses, a dropped SSH tunnel and
a broken frontend build. Each one was found by hand. The M6 test suite makes
the critical path (customer selection → /api/analyze → n8n → validated
result) verifiable in seconds and checks it automatically on every pull
request.
@vitest/coverage-v8).fetch in unit/API tests.| Unit/API tests | Live integration test | |
|---|---|---|
| Files | src/**/*.test.ts | src/**/*.integration.test.ts |
| Command | npm test / npm run test:coverage | npm run test:integration |
| n8n / Google Sheets / Gemini | Mocked; nothing external is called | Real; nothing is mocked |
| Secrets required | None | N8N_WEBHOOK_URL, N8N_WEBHOOK_SECRET |
| Runs in CI | Yes | No, it is run manually only |
The mocked tests prove how the API route behaves. They do not prove that the live workflow works; only the integration test does.
client_id, and unknown customer IDs (n8n is never called).N8N_WEBHOOK_URL / N8N_WEBHOOK_SECRET
returns 500 N8N_WEBHOOK_NOT_CONFIGURED.X-N8N-Webhook-Secret header, trimmed ID,
no-store and a timeout signal.502 N8N_INVALID_CONTRACT.502 N8N_INVALID_RESPONSE), n8n HTTP 403/404/500 (N8N_REQUEST_FAILED),
timeouts (504), network failures such as a dropped tunnel (502 N8N_UNREACHABLE) and unexpected errors (500 INTERNAL_ERROR).getCustomer), mapping of
AI risk and priority values to display tones (getTone), and navigation
matching./api/analyze. The
assertions check structure and business rules, not Gemini's exact wording.Set-Location frontend
npm ci
npm test # mocked unit/API tests
npm run test:coverage # same tests + V8 coverage (frontend/coverage/index.html)
npm run test:watch # watch mode during development
Prerequisites: the local n8n stack is running (.\scripts\start-n8n.ps1 from
the repository root), the
Customer Health - Web App MVP workflow is active, and its Google Sheets and
Gemini credentials are valid.
Set these variables in frontend/.env.local (gitignored) or in the shell:
N8N_WEBHOOK_URL: prefer the local URL,
http://localhost:5678/webhook/customer-risk-analysisN8N_WEBHOOK_SECRET: the value of the n8n Header Auth credentialSet-Location frontend
npm run test:integration
The test sends real requests for customer C005, which use Gemini quota. The
verified run took 17.31 s, but Gemini latency varies, so each test allows up to
180 s. The Web App Webhook path only reads Google Sheets and
returns the analysis: it creates no task and sends no Slack message. If a
variable is missing, the test fails with an explicit "not configured" error
instead of being skipped.
The public deployment (Vercel → VPS Nginx → reverse SSH tunnel → local n8n) depends on that local machine and tunnel being up, so the live test is a manual check, not a CI gate.
.github/workflows/tests.yml runs on pushes to
main and on pull requests. It uses Node.js 24 and runs npm ci, then
npm run lint, then npm run test:coverage in frontend/. The HTML coverage
report is uploaded as the coverage-report artifact. The workflow needs no
secrets and never runs the live integration test. The production build is still
run by Vercel on deploy.
CI passed on pull request #2
(56 tests passed), and the PR was merged into main on 2026-10-30. The push
run on main after the merge also passed.
| Suite | Where it ran | Result |
|---|---|---|
| Unit/API tests (mocked) | Locally and in GitHub Actions CI | 56 passed, 0 failed, 0 skipped |
| Live integration tests (real n8n, Google Sheets and Gemini) | Locally only, run manually; not in CI | 3 passed, 0 failed (17.31 s) |
The 3 live tests are: the Webhook rejects an unauthenticated request; C005 is
analyzed through the n8n Webhook; and C005 is analyzed through the Next.js
/api/analyze route.
Measured from the mocked unit/API suite (npm run test:coverage); the live
tests are not included in the coverage figures:
| Statements | Branches | Functions | Lines |
|---|---|---|---|
| 95.38% | 90.47% | 90.90% | 95.31% |
Scope: src/app/api/**, src/lib/** and src/components/RiskBadge.tsx
only. React pages and the other UI components are not measured, so this is
not whole-application coverage. Per-file figures, the mutation check and the
live test result are in the
M6 test report.
See M6 testing submission for the path justification and reflection.