Deliverable Standard
Handoff
sample.
Type
Redacted client deliverable
Stack
n8n · Postgres · Slack
Support
30-day post-handoff window
Why this exists
Most n8n freelancers finish a build and hand over a Notion page with screenshots. When something breaks in month two, the client can't diagnose, and the “own it in-house” promise fails. This document is what I send instead.
Client names, project codes, and specific integration identifiers have been replaced. The structure, wording, and level of specificity are identical to what shipped on the source project.
n8n Automation Handoff — PROJ-2026-HC01
- Delivery date
- 2026-05-14
- Handed off to
- Head of Operations
- Handoff engineer
- Priyanshu Kumar
- Support window
- 30 days · email + Slack
- Repo
- client-org/n8n-config (transferred)
1. What this system does
This system monitors state-by-state regulatory changes for a US healthcare consulting practice operating across all 50 US states. Every 6 hours, it polls 47 public regulator sources, deduplicates against previously-seen items, classifies each change with an LLM into 8 categories, and posts notifications to a Slack channel for the compliance team to review.
Every step is idempotent. Every failure lands in a review lane or a Slack alert. Nothing silently drops.
2. Workflow inventory
Six workflows, all version-controlled as JSON in the repo under /n8n-config/workflows/.
| # | Workflow | Trigger | Runs/day | Owner |
|---|---|---|---|---|
| 1 | regulator_poll_primary | cron 0 */6 * * * | 4 | ops |
| 2 | regulator_dedup | webhook (chained) | ~4 | ops |
| 3 | regulator_classify_llm | webhook (chained) | ~4 | ops |
| 4 | slack_publish | webhook (chained) | ~4 | ops |
| 5 | error_workflow_global | error event (all) | as needed | ops |
| 6 | health_check | cron 0 0 * * * | 1 | ops |
3. Credentials
Credentials are stored in n8n's encrypted credential store. They never appear in workflow JSON. Rotation runbook: /runbooks/credential_rotation.md.
| Credential | Type | Rotation cadence |
|---|---|---|
| postgres_prod | Postgres | Quarterly |
| gemini_api_key | HTTP Header Auth | On Google's cadence |
| slack_bot_token | OAuth2 | Annually |
| smtp_alerts | SMTP | On password change only |
4. Error handling design
Every workflow has an Error Workflow attached (n8n's native mechanism, Settings → Error Workflow). The global error workflow (error_workflow_global) does three things:
- Writes the error, stack trace, and workflow context to a Postgres
errorstable (append-only). - Sends a Slack message to
#ops-alertswith the workflow name, node name, and the first 200 chars of the error message. - If the error is on the LLM classifier (a known intermittent failure mode), routes the item to a
human_reviewqueue instead of dropping it.
No workflow silently drops data. If a step fails, either a human is notified or the item lands in review.
5. Idempotency guarantees
Regulator polling
Each item has a stable source_id (regulator URL + item hash). Postgres unique constraint on (source_id, poll_date) — re-runs on the same day are no-ops.
CREATE UNIQUE INDEX idx_regulator_items_dedup ON regulator_items (source_id, poll_date); INSERT INTO regulator_items (source_id, poll_date, payload, ...) VALUES (...) ON CONFLICT (source_id, poll_date) DO NOTHING RETURNING id;
LLM classification
Each classification is idempotent because the input hash is the row key. Re-classifying the same item returns the cached classification (with a cached_at timestamp), not a new LLM call.
Slack publish
Each (item_id, channel_id) is upserted into a published table before the message is sent. If the workflow retries after a send failure, the second attempt is a no-op.
6. Monitoring
- Uptime: n8n's built-in workflow execution log. Retained 30 days.
- Latency: p50 = 2.3s, p95 = 8.1s end-to-end on the 30-day rolling window.
- Cost: Google Gemini API costs tracked via the
classificationstable. Current run rate: ~$14/month. - Health check:
health_checkworkflow runs daily, writes an OK row, and pings a dead-man's-switch service. If no OK for 24 hours, alert to#ops-alerts.
7. Known limitations
Being honest here so nobody's surprised. Three known limits, with mitigations:
Regulator sources 42 and 47 change HTML structure every 2-3 months.
When they do, the parser breaks silently — it returns zero items rather than an error, because the CSS selectors still resolve (to nothing).
Mitigation
Weekly review of item counts per source (Slack digest every Monday). Anomaly (source with zero items for 3 consecutive polls) triggers manual inspection.
LLM classifier misclassifies 'proposed rule' vs 'final rule' ~4% of the time.
Both types share vocabulary; only structural context in the regulator's document distinguishes them.
Mitigation
Confidence threshold set so borderline cases land in the human_review queue rather than being posted to Slack. The compliance team reviews the queue daily.
Slack channel is not archived — items disappear after Slack's retention window.
This is a Slack tier limitation, not an n8n one. The compliance team wanted Slack as the primary interface, so this design accepts the trade.
Mitigation
Long-term archive is the Postgres published table (indefinite retention). A read-only Grafana dashboard is exposed for historical review.
8. Runbook
All runbooks live in /runbooks/ in the repo. The four most likely to be needed:
- /runbooks/add_regulator_source.md — add a new regulator
- /runbooks/classifier_debug.md — investigate persistent classification misfires
- /runbooks/db_restore.md — restore Postgres from backup
- /runbooks/credential_rotation.md — rotate any of the four credentials
9. Handover checklist
- ✓All workflows imported into client's n8n instance
- ✓All credentials created and tested in client's n8n
- ✓Repo transferred to client's GitHub org (client-org/n8n-config)
- ✓Slack bot installed in client's workspace
- ✓Postgres provisioned in client's cloud (Supabase)
- ✓30-day support window active — Slack channel #ops-priyanshu-support
- ✓Runbooks documented in /runbooks/
- ✓Health-check dead-man's-switch configured
End of handoff.