If you run a dealership, fleet operation, or mobility company, you're managing a staggering amount of operational data: customer service histories, vehicle inventories, telematics feeds, appointment calendars, pricing benchmarks. And you're probably still handling chunks of it manually — reminder emails sent by hand, pricing checked manually, fleet alerts noticed late.
Here are 5 complete n8n workflows to automate the most time-consuming parts of automotive and mobility operations — with full import-ready JSON for each.
Why n8n for Automotive & Mobility?
Your operational data is commercially sensitive. Customer PII, vehicle service histories, DMS exports, fleet GPS feeds, dealer pricing strategies — routing any of this through Zapier or Make.com means it passes through third-party cloud servers.
Self-hosted n8n keeps everything inside your infrastructure. It connects to your DMS, telematics platform, scheduling system, and CRM without any data leaving your network. And unlike cron scripts, n8n gives you visual debugging, built-in retry logic, and version-controlled workflow JSON.
Workflow 1: Vehicle Service Reminder Pipeline
Use case: Proactively remind customers when their vehicle is due for service — before they go to a competitor.
Trigger: Daily at 8 AM
Logic:
- Pull customer/vehicle data from Google Sheets (or your DMS via HTTP Request node)
- Calculate days since last service and estimated mileage interval
- Filter: service due within 7 days OR mileage within 500 miles of interval
- Send personalized Gmail reminders with service booking link
- Log outreach to Sheets with
reminder_sent_attimestamp to prevent duplicate sends
{
"name": "Vehicle Service Reminder Pipeline",
"nodes": [
{
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 8 * * *" }] } },
"name": "Daily 8AM Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [240, 300]
},
{
"parameters": {
"operation": "read",
"documentId": "YOUR_SHEET_ID",
"sheetName": "customers",
"options": {}
},
"name": "Get Customers",
"type": "n8n-nodes-base.googleSheets",
"position": [460, 300]
},
{
"parameters": {
"functionCode": "const today = new Date();\nconst SERVICE_INTERVAL_DAYS = 180;\nreturn items.filter(item => {\n const lastService = new Date(item.json.last_service_date);\n const daysSince = Math.floor((today - lastService) / 86400000);\n const daysUntilDue = SERVICE_INTERVAL_DAYS - daysSince;\n const notReminded = !item.json.reminder_sent_at || (today - new Date(item.json.reminder_sent_at)) > 30 * 86400000;\n item.json.days_until_due = daysUntilDue;\n return daysUntilDue <= 7 && notReminded;\n});"
},
"name": "Filter Due for Service",
"type": "n8n-nodes-base.code",
"position": [680, 300]
},
{
"parameters": {
"fromEmail": "service@yourdealership.com",
"toEmail": "={{ $json.email }}",
"subject": "={{ $json.first_name }}, your {{ $json.vehicle_year }} {{ $json.vehicle_make }} {{ $json.vehicle_model }} is due for service",
"emailType": "html",
"message": "<p>Hi {{ $json.first_name }},</p><p>Your {{ $json.vehicle_year }} {{ $json.vehicle_make }} {{ $json.vehicle_model }} is due for its next service {{ $json.days_until_due <= 0 ? 'now' : 'in ' + $json.days_until_due + ' days' }}.</p><p><a href='https://yourdealership.com/book'>Book your service appointment →</a></p><p>Our service team is ready to get you back on the road fast.</p>"
},
"name": "Send Service Reminder",
"type": "n8n-nodes-base.gmail",
"position": [900, 300]
},
{
"parameters": {
"operation": "update",
"documentId": "YOUR_SHEET_ID",
"sheetName": "customers",
"options": { "valueInputOption": "RAW" }
},
"name": "Log Reminder Sent",
"type": "n8n-nodes-base.googleSheets",
"position": [1120, 300]
}
],
"connections": {
"Daily 8AM Trigger": { "main": [[{ "node": "Get Customers", "type": "main", "index": 0 }]] },
"Get Customers": { "main": [[{ "node": "Filter Due for Service", "type": "main", "index": 0 }]] },
"Filter Due for Service": { "main": [[{ "node": "Send Service Reminder", "type": "main", "index": 0 }]] },
"Send Service Reminder": { "main": [[{ "node": "Log Reminder Sent", "type": "main", "index": 0 }]] }
}
}
Result: Customers get timely reminders, service bays stay booked, and you reduce lost service revenue to competitors.
Workflow 2: Fleet Telematics Alert
Use case: Instantly notify fleet managers when a vehicle needs attention — breakdown, speeding, fuel critical, or off-route.
Trigger: Every 5 minutes (Schedule node)
Logic:
- Poll your telematics API (or read from a Sheets/database feed updated by your GPS system)
- Classify each alert: CRITICAL (breakdown/SOS), HIGH (off-route >5km, fuel <10%), MEDIUM (speeding >20km/h over limit), LOW (idle >30min)
- Route CRITICAL → Slack #fleet-critical + Gmail driver manager immediately
- Route HIGH → Slack #fleet-ops within 1 alert cycle
- Route MEDIUM/LOW → batched daily digest
- Log all alerts to Sheets with
vehicle_id,driver_name,alert_type,coordinates,ts
{
"name": "Fleet Telematics Alert",
"nodes": [
{
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "*/5 * * * *" }] } },
"name": "Every 5 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [240, 300]
},
{
"parameters": {
"url": "https://your-telematics-api.com/v1/alerts/active",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {}
},
"name": "Fetch Telematics Alerts",
"type": "n8n-nodes-base.httpRequest",
"position": [460, 300]
},
{
"parameters": {
"functionCode": "return items.flatMap(item => {\n const alerts = Array.isArray(item.json.alerts) ? item.json.alerts : [];\n return alerts.map(alert => {\n let severity = 'LOW';\n if (['breakdown', 'sos', 'accident'].includes(alert.type)) severity = 'CRITICAL';\n else if (alert.type === 'off_route' && alert.deviation_km > 5) severity = 'HIGH';\n else if (alert.type === 'fuel_low' && alert.fuel_pct < 10) severity = 'HIGH';\n else if (alert.type === 'speeding' && alert.over_limit_kmh > 20) severity = 'MEDIUM';\n return { json: { ...alert, severity, ts: new Date().toISOString() } };\n });\n})"
},
"name": "Classify Severity",
"type": "n8n-nodes-base.code",
"position": [680, 300]
},
{
"parameters": {
"conditions": { "string": [{ "value1": "={{ $json.severity }}", "operation": "equal", "value2": "CRITICAL" }] }
},
"name": "IF Critical",
"type": "n8n-nodes-base.if",
"position": [900, 300]
},
{
"parameters": {
"channel": "#fleet-critical",
"text": "🚨 CRITICAL FLEET ALERT\nVehicle: {{ $json.vehicle_id }} | Driver: {{ $json.driver_name }}\nType: {{ $json.type.toUpperCase() }}\nLocation: {{ $json.coordinates }}\nTime: {{ $json.ts }}\nAction required immediately."
},
"name": "Slack Critical Alert",
"type": "n8n-nodes-base.slack",
"position": [1120, 200]
},
{
"parameters": {
"channel": "#fleet-ops",
"text": "⚠️ Fleet Alert [{{ $json.severity }}]: {{ $json.vehicle_id }} — {{ $json.type }} at {{ $json.ts }}"
},
"name": "Slack Ops Alert",
"type": "n8n-nodes-base.slack",
"position": [1120, 400]
}
],
"connections": {
"Every 5 Minutes": { "main": [[{ "node": "Fetch Telematics Alerts", "type": "main", "index": 0 }]] },
"Fetch Telematics Alerts": { "main": [[{ "node": "Classify Severity", "type": "main", "index": 0 }]] },
"Classify Severity": { "main": [[{ "node": "IF Critical", "type": "main", "index": 0 }]] },
"IF Critical": {
"main": [
[{ "node": "Slack Critical Alert", "type": "main", "index": 0 }],
[{ "node": "Slack Ops Alert", "type": "main", "index": 0 }]
]
}
}
}
Result: Fleet managers know about critical incidents within 5 minutes instead of hours — reducing liability, downtime, and SLA breaches.
Workflow 3: Appointment & Test Drive Reminder
Use case: Reduce no-shows for test drives and service appointments with automated 24h + 2h reminders.
Trigger: Every hour
Logic:
- Read appointment Sheets (or pull from scheduling API)
- Code node: filter appointments in 23–25h window AND 1.75–2.25h window (avoids duplicate sends)
- Check
reminded_24h/reminded_2hflags to prevent re-sends - Send personalized Gmail with appointment details, directions link, reschedule link
- Update flags in Sheets
{
"name": "Appointment & Test Drive Reminder",
"nodes": [
{
"parameters": { "rule": { "interval": [{ "field": "hours", "hoursInterval": 1 }] } },
"name": "Hourly Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [240, 300]
},
{
"parameters": {
"operation": "read",
"documentId": "YOUR_SHEET_ID",
"sheetName": "appointments",
"options": {}
},
"name": "Get Appointments",
"type": "n8n-nodes-base.googleSheets",
"position": [460, 300]
},
{
"parameters": {
"functionCode": "const now = new Date();\nreturn items.reduce((acc, item) => {\n const appt = new Date(item.json.appointment_datetime);\n const hoursAway = (appt - now) / 3600000;\n if (hoursAway >= 23 && hoursAway <= 25 && !item.json.reminded_24h) {\n acc.push({ json: { ...item.json, reminder_type: '24h' } });\n } else if (hoursAway >= 1.75 && hoursAway <= 2.25 && !item.json.reminded_2h) {\n acc.push({ json: { ...item.json, reminder_type: '2h' } });\n }\n return acc;\n}, []);"
},
"name": "Filter Upcoming Appointments",
"type": "n8n-nodes-base.code",
"position": [680, 300]
},
{
"parameters": {
"fromEmail": "appointments@yourdealership.com",
"toEmail": "={{ $json.customer_email }}",
"subject": "={{ $json.reminder_type === '24h' ? 'Reminder: Your appointment tomorrow' : 'Your appointment is in 2 hours' }} — {{ $json.appointment_type }}",
"emailType": "html",
"message": "<p>Hi {{ $json.customer_name }},</p><p>Just a reminder: your <strong>{{ $json.appointment_type }}</strong> is scheduled for <strong>{{ $json.appointment_datetime }}</strong>.</p><p>📍 <a href='https://maps.google.com/?q=YOUR+DEALERSHIP+ADDRESS'>Get directions</a> | <a href='https://yourdealership.com/reschedule?id={{ $json.appointment_id }}'>Need to reschedule?</a></p><p>See you soon!</p>"
},
"name": "Send Reminder Email",
"type": "n8n-nodes-base.gmail",
"position": [900, 300]
}
],
"connections": {
"Hourly Trigger": { "main": [[{ "node": "Get Appointments", "type": "main", "index": 0 }]] },
"Get Appointments": { "main": [[{ "node": "Filter Upcoming Appointments", "type": "main", "index": 0 }]] },
"Filter Upcoming Appointments": { "main": [[{ "node": "Send Reminder Email", "type": "main", "index": 0 }]] }
}
}
Result: No-show rates drop by 30–50% — every appointment that shows up is recovered revenue.
Workflow 4: Daily Inventory & Sales Report
Use case: Give the dealer principal and sales manager a daily digest of inventory turns, units sold, and gross profit — without pulling a manual DMS report.
Trigger: Weekdays at 5 PM
Logic:
- Pull inventory data from Sheets (sync from DMS export or DMS API)
- Calculate: units sold today, revenue, avg gross profit, days-in-stock per model
- Flag slow movers (days in stock >60 days) and low-stock models (<3 units)
- Build HTML email with sortable table
- Send to dealer principal + sales manager, post summary to Slack #management
{
"name": "Daily Inventory & Sales Report",
"nodes": [
{
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 17 * * 1-5" }] } },
"name": "Weekdays 5PM",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [240, 300]
},
{
"parameters": {
"operation": "read",
"documentId": "YOUR_SHEET_ID",
"sheetName": "inventory",
"options": {}
},
"name": "Get Inventory Data",
"type": "n8n-nodes-base.googleSheets",
"position": [460, 300]
},
{
"parameters": {
"functionCode": "const today = new Date().toDateString();\nconst allVehicles = items.map(i => i.json);\nconst soldToday = allVehicles.filter(v => v.status === 'sold' && new Date(v.sold_date).toDateString() === today);\nconst totalRevenue = soldToday.reduce((s, v) => s + Number(v.sale_price || 0), 0);\nconst totalGross = soldToday.reduce((s, v) => s + Number(v.gross_profit || 0), 0);\nconst slowMovers = allVehicles.filter(v => v.status === 'available' && Number(v.days_in_stock) > 60);\nconst lowStock = {};\nallVehicles.filter(v => v.status === 'available').forEach(v => {\n lowStock[v.model] = (lowStock[v.model] || 0) + 1;\n});\nconst lowStockModels = Object.entries(lowStock).filter(([m, c]) => c < 3).map(([m, c]) => ({ model: m, count: c }));\nreturn [{ json: { units_sold: soldToday.length, revenue: totalRevenue, avg_gross: soldToday.length ? Math.round(totalGross / soldToday.length) : 0, slow_movers: slowMovers.length, low_stock_models: lowStockModels, date: today } }];"
},
"name": "Calculate KPIs",
"type": "n8n-nodes-base.code",
"position": [680, 300]
},
{
"parameters": {
"fromEmail": "reports@yourdealership.com",
"toEmail": "principal@yourdealership.com",
"subject": "Daily Inventory & Sales Report — {{ $json.date }}",
"emailType": "html",
"message": "<h2>Daily Sales & Inventory Summary</h2><table border='1' cellpadding='6' style='border-collapse:collapse'><tr><th>Units Sold Today</th><th>Total Revenue</th><th>Avg Gross Profit</th><th>Slow Movers (>60d)</th></tr><tr><td>{{ $json.units_sold }}</td><td>${{ $json.revenue.toLocaleString() }}</td><td>${{ $json.avg_gross.toLocaleString() }}</td><td>{{ $json.slow_movers }}</td></tr></table><br><p><strong>Low Stock Models (<3 units):</strong> {{ $json.low_stock_models.map(m => m.model + ' (' + m.count + ')').join(', ') || 'None' }}</p>"
},
"name": "Email Daily Report",
"type": "n8n-nodes-base.gmail",
"position": [900, 300]
}
],
"connections": {
"Weekdays 5PM": { "main": [[{ "node": "Get Inventory Data", "type": "main", "index": 0 }]] },
"Get Inventory Data": { "main": [[{ "node": "Calculate KPIs", "type": "main", "index": 0 }]] },
"Calculate KPIs": { "main": [[{ "node": "Email Daily Report", "type": "main", "index": 0 }]] }
}
}
Result: Dealer principals get a daily pulse on the business without logging into the DMS — and slow movers get flagged before they become a 90-day problem.
Workflow 5: Competitor Pricing Monitor
Use case: Know when competitors change vehicle prices or post new deals — without manually checking listings every morning.
Trigger: Daily at 8 AM
Logic:
- Read target vehicle specs and competitor listing URLs from Sheets
- HTTP Request to scraping API (ScraperAPI, Bright Data, or your own parser) for each listing
- Code: extract current price, compare to
last_pricein Sheets - If price changed >2%: post to Slack #management with delta, update Sheets
- If price unchanged: update
last_checkedfield, no alert - Weekly digest of all competitor price movements emailed to sales manager
{
"name": "Competitor Pricing Monitor",
"nodes": [
{
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 8 * * *" }] } },
"name": "Daily 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [240, 300]
},
{
"parameters": {
"operation": "read",
"documentId": "YOUR_SHEET_ID",
"sheetName": "competitor_listings",
"options": {}
},
"name": "Get Competitor Listings",
"type": "n8n-nodes-base.googleSheets",
"position": [460, 300]
},
{
"parameters": {
"url": "=https://api.scraperapi.com/?api_key=YOUR_KEY&url={{ encodeURIComponent($json.listing_url) }}",
"options": { "timeout": 30000 }
},
"name": "Fetch Listing Page",
"type": "n8n-nodes-base.httpRequest",
"position": [680, 300]
},
{
"parameters": {
"functionCode": "const listing = $('Get Competitor Listings').first().json;\nconst html = $json.data || '';\nconst priceMatch = html.match(/\\$(\\d[\\d,]+)/);\nconst currentPrice = priceMatch ? Number(priceMatch[1].replace(/,/g, '')) : null;\nconst lastPrice = Number(listing.last_price);\nconst changePct = lastPrice ? Math.round((currentPrice - lastPrice) / lastPrice * 100) : 0;\nreturn [{ json: { ...listing, current_price: currentPrice, last_price: lastPrice, change_pct: changePct, price_changed: Math.abs(changePct) > 2, checked_at: new Date().toISOString() } }];"
},
"name": "Extract & Compare Price",
"type": "n8n-nodes-base.code",
"position": [900, 300]
},
{
"parameters": {
"conditions": { "boolean": [{ "value1": "={{ $json.price_changed }}", "value2": true }] }
},
"name": "IF Price Changed",
"type": "n8n-nodes-base.if",
"position": [1120, 300]
},
{
"parameters": {
"channel": "#management",
"text": "📊 Competitor Price Change Detected\n{{ $json.competitor_name }} | {{ $json.vehicle_year }} {{ $json.vehicle_make }} {{ $json.vehicle_model }}\nWas: ${{ $json.last_price.toLocaleString() }} → Now: ${{ $json.current_price.toLocaleString() }} ({{ $json.change_pct > 0 ? '+' : '' }}{{ $json.change_pct }}%)\n{{ $json.listing_url }}"
},
"name": "Slack Price Alert",
"type": "n8n-nodes-base.slack",
"position": [1340, 200]
}
],
"connections": {
"Daily 8AM": { "main": [[{ "node": "Get Competitor Listings", "type": "main", "index": 0 }]] },
"Get Competitor Listings": { "main": [[{ "node": "Fetch Listing Page", "type": "main", "index": 0 }]] },
"Fetch Listing Page": { "main": [[{ "node": "Extract & Compare Price", "type": "main", "index": 0 }]] },
"Extract & Compare Price": { "main": [[{ "node": "IF Price Changed", "type": "main", "index": 0 }]] },
"IF Price Changed": {
"main": [
[{ "node": "Slack Price Alert", "type": "main", "index": 0 }],
[]
]
}
}
}
Result: Your sales team knows about competitor price drops the same morning they happen — not three days later.
The Self-Hosting Advantage for Automotive
Automotive data is commercially and legally sensitive:
| Data Type | Why it can't leave your network |
|---|---|
| Customer PII + vehicle histories | CCPA, GDPR, dealer licensing agreements |
| DMS pricing & gross profit | Competitive intelligence — OEMs prohibit sharing |
| Fleet telematics & GPS | Driver privacy, insurance liability |
| Financing & credit data | FCRA, GLBA compliance |
| Competitor intelligence | Trade secrets |
Zapier and Make.com route all workflow data through their cloud. Self-hosted n8n keeps every byte inside your infrastructure — on-premise or private VPC.
n8n vs. Zapier/Make for automotive:
| Feature | n8n (self-hosted) | Zapier | Make.com |
|---|---|---|---|
| Data stays in your network | ✅ Yes | ❌ No | ❌ No |
| DMS/telematics API integration | ✅ Any HTTP API | ⚠️ Limited | ⚠️ Limited |
| Cost at 50K ops/month | $0 | ~$299/mo | ~$99/mo |
| Workflow version control | ✅ Git JSON | ❌ No | ❌ No |
| Complex branching logic | ✅ Visual + code | ⚠️ Limited | ⚠️ Limited |
Get the Ready-to-Use Templates
All 5 workflows above (plus 10 more) are available as import-ready JSON files at stripeai.gumroad.com — copy JSON, paste into n8n, update your credentials, deploy.
Templates include setup guides for connecting your DMS API, telematics platform, and Google Sheets as a data layer.
What to Build Next
Once these 5 are running, the natural extensions are:
- Trade-in valuation automation — pull Black Book / Manheim data via API, auto-calculate trade offers
- Multi-location inventory sync — sync stock levels across dealer group locations in real time
- Warranty claim intake — webhook from service system → classify claim type → route to warranty team
- Customer lifetime value tracker — aggregate service + sales history per customer → flag VIPs for targeted outreach
n8n's HTTP Request node connects to virtually any automotive API — CDK Global, Reynolds & Reynolds, DealerSocket, RouteOne, Manheim MMR, Black Book, Cox Automotive data feeds.
Using n8n for automotive or fleet operations? Drop your workflow in the comments — would love to see what others are building.
Top comments (0)