DEV Community

Alex Kane
Alex Kane

Posted on

n8n for IoT Platform SaaS: 5 Automations That Scale Device Ops and Keep Telemetry In-House (Free Workflow JSON)

If you're building an IoT platform — device management SaaS, fleet telematics, industrial IoT, smart building software — you're dealing with data volumes and latency requirements that make Zapier and Make.com non-starters.

A single IoT deployment can generate millions of webhook events per day. Zapier Pro gives you 2,000 tasks/month. The math doesn't work.

The compliance story is worse: device telemetry includes GPS location data (CCPA/GDPR), industrial sensor readings (NIST 800-82 for critical infrastructure), and patient vitals in health monitoring (HIPAA). Routing any of that through a third-party cloud automation tool creates a new GDPR Art.28 sub-processor relationship — and for government or defense IoT, it may violate data handling regulations entirely.

n8n self-hosted is the only automation layer that makes sense for IoT SaaS vendors: it runs inside your VPC, processes events at sub-100ms latency, handles millions of messages per day at zero marginal cost, and you control where every byte lands.

Here are 5 workflows every IoT platform vendor should automate — with full import-ready JSON.


Why IoT SaaS Teams Self-Host n8n

Factor n8n (self-hosted) Zapier Make.com
Data hosting Your VPC — telemetry stays inside Zapier US cloud Make EU/US cloud
Pricing at 1M events/day ~$20/mo VPS Enterprise (contact sales) Enterprise (contact sales)
Webhook latency <100ms in-VPC 2–15s polling/queue 2–10s polling/queue
Edge deployment Runs on Raspberry Pi, K3s, Jetson Cloud only Cloud only
GDPR Art.28 sub-processor None added Zapier becomes sub-processor Make becomes sub-processor
ITAR/NIST 800-82 compliance Achievable on-prem Not achievable Not achievable

Workflow 1: Device Fleet Health Monitor & Offline Alert

The problem: Thousands of devices. Some go offline or degrade. You need to know immediately — before your customer files a support ticket.

The workflow:

  • Trigger: Schedule node — every 5 minutes
  • Fetch devices: HTTP Request node — GET your device management API /api/devices?status=all
  • Classify & dedup: Code node
const devices = $input.all().map(i => i.json);
const now = Date.now();
const seen = $getWorkflowStaticData("node");
const issues = [];

for (const device of devices) {
  const minsSince = (now - new Date(device.last_seen_at).getTime()) / 60000;
  let status = "OK", severity = null;

  if (!device.connected && minsSince > 60) { status = "OFFLINE"; severity = "CRITICAL"; }
  else if (!device.connected && minsSince > 15) { status = "OFFLINE"; severity = "HIGH"; }
  else if (device.error_rate_pct > 5) { status = "ERROR"; severity = "HIGH"; }
  else if (device.error_rate_pct > 1 || device.latency_ms > 2000) { status = "DEGRADED"; severity = "MEDIUM"; }

  if (status !== "OK" && !seen[device.device_id]) {
    seen[device.device_id] = Date.now();
    issues.push({ ...device, status, severity });
  }
  if (status === "OK") delete seen[device.device_id];
}
return issues.map(d => ({ json: d }));
Enter fullscreen mode Exit fullscreen mode
  • Alert: Slack node — CRITICAL devices to #device-ops-critical, others to #device-ops
  • Log: Postgres node — INSERT INTO device_health_events (device_id, customer_id, status, severity, ts)

Workflow 2: New Customer Device Onboarding Drip

The problem: New IoT customers need to get their first device online fast. Time-to-first-device is your most important activation metric — every day of delay is churn risk.

The workflow:

  • Trigger: Google Sheets Trigger — new row added to new_customers sheet (or CRM webhook)
  • Day 0: Gmail — API credentials + SDK quickstart guide
  • Day 0: Slack DM to CSM — New IoT customer: {{company_name}} ({{device_type}}). Target: first device online within 48h.
  • Wait: 3 days
  • Day 3: Gmail — check-in: "Have you connected your first device? Reply if you hit any issues."
  • Wait: 4 days
  • Day 7: Gmail — "Here's how to set up fleet dashboards, OTA firmware updates, and device alerting."
  • Complete: Google Sheets — mark onboarding_status = 'drip_complete'

Import-ready JSON:

{
  "name": "IoT Customer Onboarding Drip",
  "nodes": [
    {"id":"1","name":"Sheets Trigger","type":"n8n-nodes-base.googleSheetsTrigger","position":[240,300],"parameters":{"sheetId":"YOUR_SHEET_ID","range":"new_customers!A:Z"}},
    {"id":"2","name":"Gmail Day0","type":"n8n-nodes-base.gmail","position":[460,300],"parameters":{"to":"={{$json.customer_email}}","subject":"Your IoT Platform API credentials + quickstart","message":"Hi {{$json.contact_name}},\n\nYour API key: {{$json.api_key}}\nSDK docs: https://docs.yourplatform.io/sdk\n\nConnect your first device in 10 min.\n\nTeam"}},
    {"id":"3","name":"Slack CSM","type":"n8n-nodes-base.slack","position":[460,420],"parameters":{"channel":"={{$json.csm_slack_id}}","text":"New IoT customer: {{$json.company_name}} ({{$json.device_type}}). Target: first device online 48h."}},
    {"id":"4","name":"Wait 3d","type":"n8n-nodes-base.wait","position":[680,300],"parameters":{"amount":3,"unit":"days"}},
    {"id":"5","name":"Gmail Day3","type":"n8n-nodes-base.gmail","position":[900,300],"parameters":{"to":"={{$json.customer_email}}","subject":"Have you connected your first device?","message":"Hi {{$json.contact_name}},\n\nChecking in — first device connected yet? Reply if you need help, we respond in < 2h.\n\nTeam"}},
    {"id":"6","name":"Wait 4d","type":"n8n-nodes-base.wait","position":[1120,300],"parameters":{"amount":4,"unit":"days"}},
    {"id":"7","name":"Gmail Day7","type":"n8n-nodes-base.gmail","position":[1340,300],"parameters":{"to":"={{$json.customer_email}}","subject":"Day 7: fleet dashboards, OTA updates, alerting","message":"Hi {{$json.contact_name}},\n\nBy now you should have telemetry flowing. Next steps:\n- Fleet dashboards\n- OTA firmware pipelines\n- Device health alerting\n\nGuide: https://docs.yourplatform.io/fleet-ops\n\nTeam"}},
    {"id":"8","name":"Mark Complete","type":"n8n-nodes-base.googleSheets","position":[1560,300],"parameters":{"operation":"update","sheetId":"YOUR_SHEET_ID","range":"new_customers","data":{"onboarding_status":"drip_complete"}}}
  ],
  "connections":{"Sheets Trigger":{"main":[[{"node":"Gmail Day0","type":"main","index":0},{"node":"Slack CSM","type":"main","index":0}]]},"Gmail Day0":{"main":[[{"node":"Wait 3d","type":"main","index":0}]]},"Wait 3d":{"main":[[{"node":"Gmail Day3","type":"main","index":0}]]},"Gmail Day3":{"main":[[{"node":"Wait 4d","type":"main","index":0}]]},"Wait 4d":{"main":[[{"node":"Gmail Day7","type":"main","index":0}]]},"Gmail Day7":{"main":[[{"node":"Mark Complete","type":"main","index":0}]]}}
}
Enter fullscreen mode Exit fullscreen mode

Workflow 3: Device Telemetry Anomaly Alert

The problem: A sensor starts reading out of range. Battery draining 3x faster than normal. You need to catch this before your customer sees it in their dashboard.

The workflow:

  • Trigger: Schedule node — every 15 minutes
  • Baseline query: Postgres — SELECT device_id, metric_name, AVG(value) as avg_7d, STDDEV(value) as stddev_7d FROM telemetry_readings WHERE ts > NOW() - INTERVAL '7 days' GROUP BY device_id, metric_name
  • Recent query: Postgres — last 1-hour average per device/metric
  • Classify: Code node — z-score anomaly detection
const baseline = $input.all()[0].json;
const recent = $input.all()[1].json;

const bMap = {};
for (const b of baseline) bMap[`${b.device_id}:${b.metric_name}`] = b;

const anomalies = [];
for (const r of recent) {
  const b = bMap[`${r.device_id}:${r.metric_name}`];
  if (!b || b.stddev_7d === 0) continue;
  const z = Math.abs(r.avg_1h - b.avg_7d) / b.stddev_7d;
  let severity = z >= 3 ? "CRITICAL" : z >= 2 ? "HIGH" : z >= 1.5 ? "WATCH" : null;
  if (severity) anomalies.push({ ...r, baseline_avg: b.avg_7d, zscore: z.toFixed(2), severity });
}

const seen = $getWorkflowStaticData("node");
const newAnomalies = anomalies.filter(a => !seen[`${a.device_id}:${a.metric_name}`]);
anomalies.forEach(a => { seen[`${a.device_id}:${a.metric_name}`] = Date.now(); });
return newAnomalies.map(a => ({ json: a }));
Enter fullscreen mode Exit fullscreen mode
  • Alert: Slack — CRITICAL to #iot-ops-critical, others to #iot-ops
  • Log: Postgres — INSERT INTO device_anomaly_log

Workflow 4: OTA Firmware Update Pipeline Monitor

The problem: You push firmware to 50,000 devices. Three hours in, 8% are failing to update. You need to know before it becomes a mass support incident.

The workflow:

  • Trigger: Schedule node — every 5 minutes
  • Fetch active rollouts: HTTP Request node — GET /api/firmware/rollouts?status=in_progress
  • Classify: Code node
const rollouts = $input.first().json.rollouts || [];
const issues = [];

for (const r of rollouts) {
  const failPct = (r.failed_devices / r.total_devices) * 100;
  const completePct = (r.completed_devices / r.total_devices) * 100;
  const elapsedH = (Date.now() - new Date(r.started_at).getTime()) / 3600000;

  let status = "PROGRESSING";
  if (failPct > 10) status = "ROLLOUT_FAILED";
  else if (failPct > 3) status = "DEGRADED";
  else if (elapsedH > r.expected_hours * 1.5 && completePct < 50) status = "STALLED";

  if (status !== "PROGRESSING") {
    issues.push({ ...r, status, failPct: failPct.toFixed(1), completePct: completePct.toFixed(1) });
  }
}
return issues.map(i => ({ json: i }));
Enter fullscreen mode Exit fullscreen mode
  • Alert: Switch node — ROLLOUT_FAILED#firmware-ops-critical, DEGRADED/STALLED#firmware-updates
  • Log: Postgres — INSERT INTO ota_events

Workflow 5: Weekly IoT Platform KPI Dashboard

The problem: Leadership needs a Monday morning report on connected devices, message volume, uptime, and customer growth. No one should be pulling this manually.

The workflow:

  • Trigger: Schedule node — Monday 8:00 AM
  • Query: Postgres node
SELECT
  COUNT(DISTINCT device_id) AS connected_devices,
  COUNT(DISTINCT customer_id) AS active_accounts,
  SUM(messages_count) AS messages_this_week,
  ROUND(AVG(uptime_pct), 2) AS avg_uptime_pct,
  COUNT(DISTINCT CASE WHEN enrolled_at > NOW() - INTERVAL '7 days'
    THEN device_id END) AS new_devices_7d
FROM device_weekly_stats
WHERE week_ending = DATE_TRUNC('week', NOW())
Enter fullscreen mode Exit fullscreen mode
  • WoW comparison: Code node using $getWorkflowStaticData("global") to store last week's values and compute deltas
  • Build HTML: Code node — professional email with metrics table, color-coded WoW indicators
  • Email: Gmail node — BCC CTO, VP Eng, Head of Customer Success
  • Slack: One-liner to #platform-metrics: Weekly IoT KPIs: 127,420 devices (+3.1%), 2.1B messages (+8.4%), 99.97% uptime. Full report in your inbox.

Getting Started

Import any of these workflows via Settings → Import Workflow in your n8n instance. Replace Slack channel names, Postgres connection strings, and API endpoints with yours.

These templates are available as ready-made, import-ready packs on FlowKit:

Browse FlowKit n8n automation templates — pre-built, documented, zero configuration headaches.

Tags: n8n, iot, automation, programming

Top comments (0)