AutomationFlowsSlack & Telegram › Manage Whatsapp Vehicle Service Reminders and Bookings with Wati and Google…

Manage Whatsapp Vehicle Service Reminders and Bookings with Wati and Google…

Original n8n title: Manage Whatsapp Vehicle Service Reminders and Bookings with Wati and Google Sheets

ByJitesh Dugar @jiteshdugar on n8n.io

Streamline your automotive service center's operations with this comprehensive automation. This workflow manages the entire customer lifecycle—from automated service reminders and instant appointment booking via WhatsApp to mileage tracking and full service history logs—all…

Cron / scheduled trigger★★★★★ complexity39 nodesGoogle SheetsN8N Nodes Wati
Slack & Telegram Trigger: Cron / scheduled Nodes: 39 Complexity: ★★★★★ Added:

This workflow corresponds to n8n.io template #13733 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "6c067f10-854c-46ac-897c-4534d8cbeaae",
      "name": "\ud83d\udccb Flow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1728,
        5696
      ],
      "parameters": {
        "width": 660,
        "height": 720,
        "content": "## \ud83d\ude97 WhatsApp Vehicle Service Reminder \u2013 Booking & History Tracking\n\n**How it works:**\n1. A scheduled trigger runs every morning and checks all vehicles in Google Sheets for upcoming or overdue service (by date or mileage)\n2. Personalised reminder messages are sent via WATI to each vehicle owner\n3. Owner replies *book* \u2192 bot shows available time slots \u2192 owner picks one \u2192 appointment logged in Sheets\n4. Owner can update their mileage anytime by sending *mileage <km>* \u2014 bot recalculates next service point\n5. A second schedule runs every morning and reminds owners of appointments due tomorrow\n6. Owner can send *history* to see past services or *status* to view their vehicle card\n7. Garage staff send *logservice* to record a completed service \u2014 resets the reminder cycle\n\n**Credentials needed:** WATI (wati - templates), Google Sheets OAuth2\n\n**Google Sheets \u2014 3 tabs:**\n- **Vehicles** \u2014 one row per vehicle: phone, owner, reg, make, model, mileage, service schedule, status\n- **Appointments** \u2014 booked slots: vehicle, date, time slot, service type, status\n- **ServiceHistory** \u2014 completed services: date, mileage at service, work done, cost\n\n**Replace `YOUR_GOOGLE_SHEET_ID` in all Sheets nodes after import.**"
      },
      "typeVersion": 1
    },
    {
      "id": "098a806a-3eda-4fc6-851b-f4e22fa976ab",
      "name": "Sticky \u2013 Scheduled Reminder",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -352,
        6240
      ],
      "parameters": {
        "color": 7,
        "width": 1440,
        "height": 336,
        "content": "### 1\ufe0f\u20e3 Scheduled Service Reminder\n**Schedule Trigger \u2013 9AM Daily** fires every morning.\n**Sheets \u2013 Read All Vehicles** fetches every row from the Vehicles tab.\n**Check Service Due Code** applies two checks per vehicle: (a) nextServiceDate \u2264 today + 7 days, and (b) nextServiceMileage - currentMileage \u2264 500 km. Skips vehicles where reminderSent = Yes or status = Booked. Returns one item per due vehicle with a pre-built reminder message.\n**Sheets \u2013 Mark Reminder Sent** updates reminderSent = Yes and status = Overdue if past due date, preventing duplicate reminders on following days.\n**WATI \u2013 Send Service Reminder** sends the personalised reminder to the owner's WhatsApp with *book*, *status* and *history* reply options."
      },
      "typeVersion": 1
    },
    {
      "id": "9e5ebd30-03bf-4aa3-841e-319d3245b5ab",
      "name": "Sticky \u2013 Appointment Reminder",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1248,
        6384
      ],
      "parameters": {
        "color": 7,
        "width": 1200,
        "height": 288,
        "content": "### 2\ufe0f\u20e3 Day-Before Appointment Reminder\n**Schedule Trigger \u2013 8AM Daily** fires separately every morning.\n**Sheets \u2013 Read Appointments** fetches all rows from the Appointments tab.\n**Check Tomorrow Appointments Code** filters for rows where appointmentDate = tomorrow and status = Confirmed. Returns one item per appointment due tomorrow.\n**WATI \u2013 Send Appointment Reminder** sends a reminder with the service time and a *cancel* option."
      },
      "typeVersion": 1
    },
    {
      "id": "b70b22d5-8872-4138-b0c1-2582a07181c7",
      "name": "Sticky \u2013 Inbound Routing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1088,
        6640
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 608,
        "content": "### 3\ufe0f\u20e3 Inbound Reply Routing\n**Wati Trigger** receives all inbound WhatsApp messages from vehicle owners and garage staff.\n**Command Router Switch** detects the reply keyword and routes to the correct path:\n- `book` \u2192 show available booking slots\n- `mileage <km>` \u2192 update odometer reading\n- `history` \u2192 show past service records\n- `status` \u2192 show vehicle details card\n- `logservice <reg> <km> <work> <cost>` \u2192 garage staff logs a completed service\n- `confirm <number>` \u2192 owner confirms a slot selection\n- anything else \u2192 help card"
      },
      "typeVersion": 1
    },
    {
      "id": "05706c60-7970-42c2-8b23-ece352f94839",
      "name": "Sticky \u2013 Book & Confirm",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        6640
      ],
      "parameters": {
        "color": 7,
        "width": 1520,
        "height": 608,
        "content": "### 4\ufe0f\u20e3 Book Appointment & Confirm Slot\n**Sheets \u2013 Find Vehicle for Booking** reads Vehicles tab to get owner's vehicle details.\n**Parse Book & Generate Slots Code** finds the owner's vehicle by phone, generates 4 available time slots across the next 3 weekdays (Mon\u2013Fri, 10AM and 2PM), and sends a numbered slot list.\n**WATI \u2013 Send Available Slots** sends the slot menu to the owner.\n\nWhen owner replies with *1*, *2*, *3* or *4*:\n**Sheets \u2013 Find Vehicle for Confirm** re-reads vehicle details.\n**Handle Slot Confirmation Code** maps the chosen number to the regenerated slot, creates a unique appointment ID, and builds a confirmation message.\n**Sheets \u2013 Log Appointment** appends a new row to the Appointments tab with status = Confirmed.\n**WATI \u2013 Confirm Appointment** sends the booking confirmation to the owner."
      },
      "typeVersion": 1
    },
    {
      "id": "d89ab938-2d4f-45a1-bbbe-50359094498a",
      "name": "Sticky \u2013 Mileage Update",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1248,
        6800
      ],
      "parameters": {
        "color": 7,
        "width": 1200,
        "height": 336,
        "content": "### 5\ufe0f\u20e3 Mileage Update\nOwner sends: *mileage 51650*\n**Sheets \u2013 Find Vehicle for Mileage** reads the owner's current vehicle record.\n**Parse Mileage Update Code** extracts the new km value, recalculates nextServiceMileage = lastServiceMileage + serviceIntervalKm, flags as Overdue if already past the service point, and builds a response showing km remaining to next service.\n**Sheets \u2013 Update Mileage** updates currentMileage, nextServiceMileage and status in Vehicles tab.\n**WATI \u2013 Send Mileage Ack** confirms the update and warns if service is due soon."
      },
      "typeVersion": 1
    },
    {
      "id": "6fa7e405-9591-4182-acf0-854a75463146",
      "name": "Sticky \u2013 History, Status & Log",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1088,
        7280
      ],
      "parameters": {
        "color": 7,
        "width": 3136,
        "height": 400,
        "content": "### 6\ufe0f\u20e3 History \u00b7 Status \u00b7 Log Service\n**History path** \u2014 *Sheets \u2013 Read Service History* fetches the ServiceHistory tab. **Build History View Code** filters by phone, sorts newest-first, formats last 5 records with date, mileage, work done and cost. **WATI \u2013 Send History** delivers the service log card.\n\n**Status path** \u2014 *Sheets \u2013 Read Vehicle for Status* fetches the Vehicles tab. **Build Vehicle Status Code** builds a full vehicle card showing reg, model, current mileage, next service date and km, and current status badge. **WATI \u2013 Send Vehicle Status** delivers the card.\n\n**Log Service path** (garage staff) \u2014 *Parse Log Service Code* parses: *logservice MH12AB3456 52000 Oil change 2500*. **Sheets \u2013 Append Service Record** writes a new row to ServiceHistory. **WATI \u2013 Confirm Service Logged** confirms to the staff member.\n\n**Help fallback** \u2014 *WATI \u2013 Send Help* delivers the command menu when no keyword matches."
      },
      "typeVersion": 1
    },
    {
      "id": "ab162e9b-6076-4f71-a377-1ccd4f0190db",
      "name": "Schedule Trigger \u2013 8AM Daily",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1328,
        6544
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b36cb999-aa78-4eaf-b0d9-e583f15c40ee",
      "name": "Schedule Trigger \u2013 9AM Daily1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -272,
        6400
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f3e0ef52-c8d8-46a5-a0a6-f580f5b21bb4",
      "name": "Sheets \u2013 Read All Vehicles1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -32,
        6400
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=0",
          "cachedResultName": "Vehicles Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "72f5f0fe-639f-456d-8faa-6662aa89efa7",
      "name": "Check Service Due1",
      "type": "n8n-nodes-base.code",
      "position": [
        208,
        6400
      ],
      "parameters": {
        "jsCode": "const allRows = $input.all();\nconst now     = new Date();\nconst todayMs = now.getTime();\nconst sevenDaysMs = 7 * 24 * 60 * 60 * 1000;\n\nconst due = [];\nfor (const r of allRows) {\n  const v = r.json;\n  if ((v.reminderSent || '').toLowerCase() === 'yes') continue;\n  if ((v.status      || '').toLowerCase() === 'booked') continue;\n  if (!v.phone) continue;\n\n  let isDue = false; let urgency = 'normal';\n\n  if (v.nextServiceDate) {\n    try {\n      const diff = new Date(v.nextServiceDate).getTime() - todayMs;\n      if (diff <= sevenDaysMs) { isDue = true; urgency = diff < 0 ? 'overdue' : diff <= 2*86400000 ? 'critical' : 'normal'; }\n    } catch(e) {}\n  }\n  const curKm  = parseFloat(v.currentMileage || 0);\n  const nextKm = parseFloat(v.nextServiceMileage || 0);\n  if (nextKm > 0 && (nextKm - curKm) <= 500) { isDue = true; if (curKm >= nextKm) urgency = 'overdue'; }\n\n  if (!isDue) continue;\n\n  const urgencyLine = { overdue:'\u26a0\ufe0f *OVERDUE* \u2014 Please book immediately!', critical:'\ud83d\udd34 *Due in 1\u20132 days* \u2014 Book now!', normal:'\ud83d\udd14 Service due soon' }[urgency];\n  const dateStr  = v.nextServiceDate ? new Date(v.nextServiceDate).toLocaleDateString('en-IN',{day:'numeric',month:'short',year:'numeric'}) : 'Not set';\n  const kmLine   = nextKm > 0 ? `\ud83d\udee3\ufe0f Next service: *${parseInt(nextKm).toLocaleString()} km*  (now: *${parseInt(curKm).toLocaleString()} km*)` : '';\n\n  const msg = [\n    `\ud83d\udd27 *Service Reminder \u2014 ${v.vehicleReg || 'Your Vehicle'}*`,\n    `\ud83d\udc64 Hi *${v.ownerName || 'Owner'}!*`, '',\n    `\ud83d\ude97 ${v.make||''} ${v.model||''} ${v.year||''} \u00b7 *${v.vehicleReg||''}*`,\n    urgencyLine, '',\n    `\ud83d\udcc5 Date due: *${dateStr}*`, kmLine, '',\n    '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500',\n    'Reply *book* \u2192 schedule an appointment',\n    'Reply *status* \u2192 view vehicle details',\n    'Reply *history* \u2192 view past services'\n  ].filter(Boolean).join('\\n');\n\n  due.push({ json: { phone: v.phone, vehicleReg: v.vehicleReg, ownerName: v.ownerName, urgency, reminderMsg: msg } });\n}\nif (due.length === 0) return [{ json: { message: 'No vehicles due today', count: 0 } }];\nreturn due;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "34bca1ba-c9a7-4885-a88c-7105d48d1c09",
      "name": "Sheets \u2013 Mark Reminder Sent1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        448,
        6400
      ],
      "parameters": {
        "columns": {
          "value": {
            "phone": "={{ $json.phone }}",
            "status": "={{ $json.urgency === 'overdue' ? 'Overdue' : 'Active' }}",
            "reminderSent": "Yes"
          },
          "schema": [
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": true,
              "displayName": "phone",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "phone"
          ]
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=0",
          "cachedResultName": "Vehicles Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "a2cdb6c2-9e73-4b60-96db-c4562ab5a95f",
      "name": "WATI \u2013 Send Service Reminder1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        688,
        6400
      ],
      "parameters": {
        "target": "={{ $json.phone }}",
        "messageText": "={{ $('Check Service Due1').item.json.reminderMsg }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "614f5338-f786-48ba-9e6c-51cbd2e67648",
      "name": "Sheets \u2013 Read Appointments1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1568,
        6544
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1767856248,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=1767856248",
          "cachedResultName": "Appointments Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "ec881846-3be6-440e-add5-a1473362c946",
      "name": "Check Tomorrow Appointments1",
      "type": "n8n-nodes-base.code",
      "position": [
        1808,
        6544
      ],
      "parameters": {
        "jsCode": "const rows = $input.all();\nconst now  = new Date();\nconst tom  = new Date(now); tom.setDate(tom.getDate() + 1);\nconst tomStr = tom.toISOString().split('T')[0];\nconst due = rows.filter(r => (r.json.appointmentDate||'').split('T')[0] === tomStr && (r.json.status||'').toLowerCase() === 'confirmed');\nif (due.length === 0) return [{ json: { message: 'No appointments tomorrow' } }];\nreturn due.map(r => ({ json: {\n  phone: r.json.phone,\n  msg: `\ud83d\udcc5 *Appointment Reminder*\\n\\nYour service is *tomorrow!*\\n\ud83d\ude97 Reg: *${r.json.vehicleReg}*\\n\ud83d\udd27 ${r.json.serviceType || 'General Service'}\\n\u23f0 *${r.json.timeSlot}*\\n\\nPlease arrive 10 min early.\\nReply *cancel* to reschedule.`\n}}));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5e448930-4b70-428e-9b98-572609aaa5b8",
      "name": "WATI \u2013 Send Appointment Reminder1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        2048,
        6544
      ],
      "parameters": {
        "target": "={{ $json.phone }}",
        "messageText": "={{ $json.msg }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8127d61b-a6dc-457b-b280-17a9dfabcf10",
      "name": "Command Router1",
      "type": "n8n-nodes-base.switch",
      "position": [
        -768,
        6960
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Book",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "04ad63ad-a94d-485a-8cec-276f54bd6610",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.text.toLowerCase().trim() }}",
                    "rightValue": "book"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Mileage",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "a61c1ccb-883c-4a43-933d-a159ccd114fe",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    },
                    "leftValue": "={{ $json.text }}",
                    "rightValue": "mileage "
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "History",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "22f9ab06-51bd-42ca-89fd-6b072ab06a11",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.text.toLowerCase().trim() }}",
                    "rightValue": "history"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Status",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "09718233-6de6-4e4f-86e7-40364b5c5231",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.text.toLowerCase().trim() }}",
                    "rightValue": "status"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Log Service",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c8f3eaa4-8eeb-474f-b458-e46ba539f449",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    },
                    "leftValue": "={{ $json.text }}",
                    "rightValue": "logservice "
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Confirm Slot",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "11c3402f-7a1c-41d4-a854-a23a78952ee2",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    },
                    "leftValue": "={{ $json.text }}",
                    "rightValue": "confirm"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "428decda-0588-46e9-9970-057826740182",
      "name": "Sheets \u2013 Find Vehicle for Booking1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -304,
        6880
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=0",
          "cachedResultName": "Vehicles Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "e26ecfe3-40b1-428e-b861-32bd3d1bd180",
      "name": "Parse Book & Generate Slots1",
      "type": "n8n-nodes-base.code",
      "position": [
        -112,
        6880
      ],
      "parameters": {
        "jsCode": "const phone   =  $('Wati Trigger').first().json.waId|| $('Wati Trigger1').item.json.from;\nconst allRows = $input.all();\nconst vehicle = allRows.find(r => (r.json.phone||'') === phone);\nif (!vehicle) return [{ json: { phone, bookingMsg: '\u26a0\ufe0f No vehicle found. Contact the service centre to register.' }}];\nconst v = vehicle.json;\nconst now = new Date();\nconst slots = [];\nlet day = new Date(now); day.setDate(day.getDate() + 1);\nconst times = ['10:00 AM', '02:00 PM'];\nwhile (slots.length < 4) {\n  const dow = day.getDay();\n  if (dow !== 0 && dow !== 6) {\n    for (const t of times) {\n      if (slots.length < 4) {\n        const label = day.toLocaleDateString('en-IN',{weekday:'short',day:'numeric',month:'short'});\n        slots.push({ label: `${slots.length+1}\\ufe0f\\u20e3  ${label} \u2013 ${t}`, date: day.toISOString().split('T')[0], time: t });\n      }\n    }\n  }\n  day = new Date(day); day.setDate(day.getDate() + 1);\n}\nconst msg = [`\ud83d\udd27 *Book a Service Appointment*`,`\ud83d\ude97 ${v.make} ${v.model} \u00b7 *${v.vehicleReg}*`,``,`\ud83d\udcc5 *Choose a slot:*`,...slots.map(s=>s.label),``,`Reply *1*, *2*, *3* or *4* to confirm.`].join('\\n');\nreturn [{ json: { phone, vehicleReg: v.vehicleReg, make: v.make, model: v.model, ownerName: v.ownerName, bookingMsg: msg }}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "fc383e54-cff8-4905-8371-1dab9508b18f",
      "name": "WATI \u2013 Send Available Slots1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        112,
        6880
      ],
      "parameters": {
        "target": "={{ $json.phone }}",
        "messageText": "={{ $json.bookingMsg }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ae9f7c7b-0f8b-4f58-b4b0-f4e6dca6b701",
      "name": "Sheets \u2013 Find Vehicle for Confirm1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -320,
        7072
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=0",
          "cachedResultName": "Vehicles Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "41ca15c3-f6f8-437f-90a0-55a7416fd471",
      "name": "Handle Slot Confirmation1",
      "type": "n8n-nodes-base.code",
      "position": [
        -128,
        7072
      ],
      "parameters": {
        "jsCode": "const phone    = $('Wati Trigger').item.json.waId || $('Wati Trigger1').item.json.from;\nconst text     = ($('Wati Trigger').item.json.text || '').trim();\nconst choice   = parseInt(text.replace(/^confirm\\s*/i,'').trim());\nconst allRows  = $input.all();\nconst vehicle  = allRows.find(r => (r.json.phone||'') === phone);\nif (!vehicle) return [{ json: { phone, confirmMsg: '\u26a0\ufe0f Vehicle not found. Contact the service centre.' }}];\nif (isNaN(choice) || choice < 1 || choice > 4) return [{ json: { phone, confirmMsg: '\u26a0\ufe0f Reply *confirm 1*, *confirm 2*, *confirm 3* or *confirm 4* to pick a slot.\\n\\nSend *book* to see slots again.' }}];\nconst v = vehicle.json;\nconst now = new Date();\nconst slots = [];\nlet day = new Date(now); day.setDate(day.getDate() + 1);\nconst times = ['10:00 AM', '02:00 PM'];\nwhile (slots.length < 4) {\n  const dow = day.getDay();\n  if (dow !== 0 && dow !== 6) {\n    for (const t of times) { if (slots.length < 4) { slots.push({ date: day.toISOString().split('T')[0], time: t }); } }\n  }\n  day = new Date(day); day.setDate(day.getDate() + 1);\n}\nconst chosen  = slots[choice - 1];\nconst apptId  = `APT-${phone}-${Date.now()}`;\nconst dateLabel = new Date(chosen.date).toLocaleDateString('en-IN',{weekday:'long',day:'numeric',month:'long'});\nconst confirmMsg = [`\u2705 *Appointment Confirmed!*`,``,`\ud83d\udcc5 *${dateLabel}*`,`\u23f0 Time: *${chosen.time}*`,`\ud83d\ude97 ${v.make} ${v.model} \u00b7 *${v.vehicleReg}*`,`\ud83d\udd27 Full Service`,``,`We'll remind you the day before. See you then! \ud83d\ude4c`,``,`Reply *history* to view past services.`].join('\\n');\nreturn [{ json: { phone, vehicleReg: v.vehicleReg, ownerName: v.ownerName, apptId, appointmentDate: chosen.date, timeSlot: chosen.time, serviceType: 'Full Service', bookedAt: now.toISOString(), confirmMsg }}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f026b12e-50f9-40c1-947e-7fef003a86ef",
      "name": "Sheets \u2013 Log Appointment1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        112,
        7072
      ],
      "parameters": {
        "columns": {
          "value": {
            "phone": "={{ $json.phone }}",
            "apptId": "={{ $json.apptId }}",
            "status": "Confirmed",
            "bookedAt": "={{ $json.bookedAt }}",
            "timeSlot": "={{ $json.timeSlot }}",
            "vehicleReg": "={{ $json.vehicleReg }}",
            "serviceType": "={{ $json.serviceType }}",
            "appointmentDate": "={{ $json.appointmentDate }}"
          },
          "schema": [
            {
              "id": "apptId",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "apptId",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "vehicleReg",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "vehicleReg",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "serviceType",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "serviceType",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "appointmentDate",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "appointmentDate",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timeSlot",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timeSlot",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "bookedAt",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "bookedAt",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1767856248,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=1767856248",
          "cachedResultName": "Appointments Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "dc0da2c5-52b5-481f-bd33-2925ed777e0e",
      "name": "WATI \u2013 Confirm Appointment1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        352,
        7072
      ],
      "parameters": {
        "target": "={{ $('Handle Slot Confirmation1').item.json.phone }}",
        "messageText": "={{ $('Handle Slot Confirmation1').item.json.confirmMsg }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2fbbebec-6194-457c-beaa-2aa7168bb226",
      "name": "Sheets \u2013 Find Vehicle for Mileage1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1248,
        6960
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=0",
          "cachedResultName": "Vehicles Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1251302e-c942-4ca4-bc8c-3ed8c7e01e3b",
      "name": "Parse Mileage Update1",
      "type": "n8n-nodes-base.code",
      "position": [
        1488,
        6960
      ],
      "parameters": {
        "jsCode": "const phone  = $('Wati Trigger').first().json.waId || $('Wati Trigger1').item.json.from;\nconst text   = ($('Wati Trigger').first().json.text || '').trim();\nconst newKm  = parseFloat(text.split(/\\s+/)[1] || '0');\nif (!newKm || newKm <= 0) return [{ json: { phone, milMsg: '\u26a0\ufe0f Format: *mileage <km>*\\nExample: *mileage 51650*' }}];\nconst allRows = $input.all();\nconst vehicle = allRows.find(r => (r.json.phone||'') === phone);\nif (!vehicle) return [{ json: { phone, milMsg: '\u26a0\ufe0f No vehicle registered. Contact the service centre.' }}];\nconst v          = vehicle.json;\nconst intervalKm = parseFloat(v.serviceIntervalKm || 5000);\nconst lastKm     = parseFloat(v.lastServiceMileage || 0);\nconst nextKm     = lastKm + intervalKm;\nconst kmLeft     = nextKm - newKm;\nconst isOverdue  = newKm >= nextKm;\nconst urgencyLine = isOverdue\n  ? `\u26a0\ufe0f *Overdue!* ${Math.abs(Math.round(kmLeft)).toLocaleString()} km past service point!`\n  : kmLeft <= 500 ? `\ud83d\udd34 Only *${Math.round(kmLeft).toLocaleString()} km* left \u2014 book soon!`\n  : `\u2705 Next service in *${Math.round(kmLeft).toLocaleString()} km*`;\nconst milMsg = [`\ud83d\udccf *Mileage Updated*`,`\ud83d\ude97 ${v.make} ${v.model} \u00b7 ${v.vehicleReg}`,``,`\ud83d\udee3\ufe0f Current: *${newKm.toLocaleString()} km*`,`\ud83d\udee3\ufe0f Next service at: *${nextKm.toLocaleString()} km*`,urgencyLine,``,isOverdue || kmLeft <= 500 ? `Reply *book* to schedule now \ud83d\udd27` : `Keep it up! \ud83d\ude97`].join('\\n');\nreturn [{ json: { phone, vehicleReg: v.vehicleReg, currentMileage: newKm, nextServiceMileage: nextKm, newStatus: isOverdue ? 'Overdue' : 'Active', milMsg }}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "c143ac45-2b4d-446c-aef7-b3cbc2fe9a72",
      "name": "Sheets \u2013 Update Mileage1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1728,
        6960
      ],
      "parameters": {
        "columns": {
          "value": {
            "phone": "={{ $json.phone }}",
            "status": "={{ $json.newStatus }}",
            "currentMileage": "={{ $json.currentMileage }}",
            "nextServiceMileage": "={{ $json.nextServiceMileage }}"
          },
          "schema": [
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": true,
              "displayName": "phone",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "phone"
          ]
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=0",
          "cachedResultName": "Vehicles Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "4584eeb8-67fb-4381-a479-9e78ee415a0a",
      "name": "WATI \u2013 Send Mileage Ack1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        1968,
        6960
      ],
      "parameters": {
        "target": "={{ $('Parse Mileage Update1').item.json.phone }}",
        "messageText": "={{ $('Parse Mileage Update1').item.json.milMsg }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "aadfe8c1-4654-499c-9f5e-b3803bb85c30",
      "name": "Sheets \u2013 Read Service History1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -368,
        7440
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1494347229,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=1494347229",
          "cachedResultName": "ServiceHistory Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "b7ef0826-3c2d-4ea7-926f-220e52d138e9",
      "name": "Build History View1",
      "type": "n8n-nodes-base.code",
      "position": [
        -128,
        7440
      ],
      "parameters": {
        "jsCode": "const phone   = $('Wati Trigger').item.json.waId || $('Wati Trigger1').item.json.from;\nconst records = $input.all().filter(r => (r.json.phone||'') === phone)\n  .sort((a,b) => new Date(b.json.serviceDate) - new Date(a.json.serviceDate)).slice(0,5);\nif (records.length === 0) return [{ json: { phone, histMsg: '\ud83d\udd27 *No service history found.*\\n\\nOnce your vehicle is serviced, records appear here.' }}];\nconst lines = [`\ud83d\udccb *Service History*`,``];\nfor (const r of records) {\n  const dt   = r.json.serviceDate ? new Date(r.json.serviceDate).toLocaleDateString('en-IN',{day:'numeric',month:'short',year:'numeric'}) : '\u2014';\n  const km   = r.json.mileageAtService ? parseInt(r.json.mileageAtService).toLocaleString() + ' km' : '\u2014';\n  const cost = r.json.cost ? `\u20b9${parseFloat(r.json.cost).toLocaleString()}` : '\u2014';\n  lines.push(`\ud83d\udd27 *${dt}* \u00b7 ${km}`);\n  lines.push(`   ${r.json.serviceType || 'Service'}: ${r.json.workDone || '\u2014'}`);\n  lines.push(`   Cost: ${cost}`); lines.push('');\n}\nlines.push('\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'); lines.push('Reply *book* to schedule your next service.');\nreturn [{ json: { phone, histMsg: lines.join('\\n') } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "06766d32-7353-4d05-910e-af4f0abd4270",
      "name": "WATI \u2013 Send History1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        112,
        7440
      ],
      "parameters": {
        "target": "={{ $json.phone }}",
        "messageText": "={{ $json.histMsg }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "83ce8cd4-ba27-4a32-8cd7-13bdb9a0bb9a",
      "name": "Sheets \u2013 Read Vehicle for Status1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        368,
        7424
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=0",
          "cachedResultName": "Vehicles Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "6eded368-9e59-4a4d-875f-81431eed645f",
      "name": "Build Vehicle Status1",
      "type": "n8n-nodes-base.code",
      "position": [
        608,
        7424
      ],
      "parameters": {
        "jsCode": "const phone   = $('Wati Trigger').item.json.waId || $('Wati Trigger1').item.json.from;\nconst v = $input.all().find(r => (r.json.phone||'') === phone);\nif (!v) return [{ json: { phone, statusMsg: '\u26a0\ufe0f No vehicle registered.\\nContact the service centre.' }}];\nconst rec    = v.json;\nconst nextDt = rec.nextServiceDate ? new Date(rec.nextServiceDate).toLocaleDateString('en-IN',{day:'numeric',month:'short',year:'numeric'}) : 'Not set';\nconst lastDt = rec.lastServiceDate ? new Date(rec.lastServiceDate).toLocaleDateString('en-IN',{day:'numeric',month:'short',year:'numeric'}) : 'Not yet serviced';\nconst badge  = {active:'\u2705',overdue:'\ud83d\udd34',booked:'\ud83d\udcc5'}[(rec.status||'active').toLowerCase()] || '\u2705';\nconst msg = [`\ud83d\ude97 *Vehicle Status*`,``,`\ud83d\udd35 *${rec.make||''} ${rec.model||''} ${rec.year||''}*`,`\ud83d\udd35 Reg: *${rec.vehicleReg||'\u2014'}*`,`\ud83d\udd35 Owner: ${rec.ownerName||'\u2014'}`,``,`${badge} Status: *${rec.status||'Active'}*`,`\ud83d\udee3\ufe0f Current: *${parseInt(rec.currentMileage||0).toLocaleString()} km*`,``,`\ud83d\udd27 *Next Service*`,`   \ud83d\udcc5 Date: *${nextDt}*`,`   \ud83d\udee3\ufe0f At: *${parseInt(rec.nextServiceMileage||0).toLocaleString()} km*`,``,`\ud83d\udd27 *Last Service*`,`   \ud83d\udcc5 ${lastDt}`,``,`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,`Reply *book* to schedule \u00b7 *history* for records`].join('\\n');\nreturn [{ json: { phone, statusMsg: msg } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "96aa6941-f598-4459-bf77-31b0746ddbaf",
      "name": "WATI \u2013 Send Vehicle Status1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        832,
        7440
      ],
      "parameters": {
        "target": "={{ $json.phone }}",
        "messageText": "={{ $json.statusMsg }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2c703e1e-f31e-4482-bdb8-20be1333076f",
      "name": "Parse Log Service1",
      "type": "n8n-nodes-base.code",
      "position": [
        1088,
        7408
      ],
      "parameters": {
        "jsCode": "const phone = $json.waId || $json.from;\nconst text  = ($json.text || '').replace(/^logservice\\s+/i,'').trim();\nconst parts = text.split(/\\s+/);\nif (parts.length < 4) return [{ json: { phone, errMsg: '\u26a0\ufe0f Format: *logservice <reg> <km> <work> <cost>*\\nExample: *logservice MH12AB3456 52000 Oil change 2500*' }}];\nconst reg    = parts[0].toUpperCase();\nconst km     = parseFloat(parts[1]) || 0;\nconst cost   = parseFloat(parts[parts.length-1]) || 0;\nconst work   = parts.slice(2,parts.length-1).join(' ');\nconst now    = new Date();\nconst nextDt = new Date(now); nextDt.setDate(nextDt.getDate() + 180);\nreturn [{ json: { phone, reg, km, cost, work, historyId: `SH-${reg}-${now.getTime()}`, serviceDate: now.toISOString().split('T')[0], recordedAt: now.toISOString(), nextServiceDate: nextDt.toISOString().split('T')[0], logMsg: `\u2705 *Service Logged*\\n\ud83d\ude97 ${reg}\\n\ud83d\udee3\ufe0f At ${km.toLocaleString()} km\\n\ud83d\udd27 ${work}\\n\ud83d\udcb0 \u20b9${cost.toLocaleString()}` }}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "57d19ea9-6445-47f5-acac-29d2736ca072",
      "name": "Sheets \u2013 Append Service Record1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1328,
        7408
      ],
      "parameters": {
        "columns": {
          "value": {
            "cost": "={{ $json.cost }}",
            "phone": "={{ $json.phone }}",
            "workDone": "={{ $json.work }}",
            "historyId": "={{ $json.historyId }}",
            "recordedAt": "={{ $json.recordedAt }}",
            "vehicleReg": "={{ $json.reg }}",
            "serviceDate": "={{ $json.serviceDate }}",
            "serviceType": "Full Service",
            "mileageAtService": "={{ $json.km }}"
          },
          "schema": [
            {
              "id": "historyId",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "historyId",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "vehicleReg",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "vehicleReg",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "serviceDate",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "serviceDate",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "mileageAtService",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "mileageAtService",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "serviceType",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "serviceType",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "workDone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "workDone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cost",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "cost",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "recordedAt",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "recordedAt",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1494347229,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit#gid=1494347229",
          "cachedResultName": "ServiceHistory Tab"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eTlVJbSiz8P8PpjaqXgkRsZF8oeBTH_4kDtC5oaoTpI/edit?usp=drivesdk",
          "cachedResultName": "Vehicles - Wati"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "feed07d9-1360-4c53-a786-8fbfba09dcec",
      "name": "WATI \u2013 Confirm Service Logged1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        1568,
        7408
      ],
      "parameters": {
        "target": "={{ $('Parse Log Service1').item.json.phone }}",
        "messageText": "={{ $('Parse Log Service1').item.json.errMsg }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "aa67610d-ef92-4bb0-b4db-6f51a121c13d",
      "name": "WATI \u2013 Send Help1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        1792,
        7360
      ],
      "parameters": {
        "target": "={{ $json.waId }}",
        "messageText": "\ud83d\ude97 *Vehicle Service Bot*\n\nCommands:\n\n*status* \u2014 Your vehicle details\n*book* \u2014 Book a service appointment\n*confirm <n>* \u2014 Confirm slot 1, 2, 3 or 4\n*history* \u2014 View past service records\n*mileage <km>* \u2014 Update your odometer\n  e.g. *mileage 51650*\n\n*Garage staff only:*\n*logservice <reg> <km> <work> <cost>*\n  e.g. *logservice MH12AB3456 52000 Oil change 2500*"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "162d1d59-73c0-4b86-8b54-b7b362261dc5",
      "name": "Wati Trigger",
      "type": "n8n-nodes-wati.watiTrigger",
      "position": [
        -1040,
        7040
      ],
      "parameters": {
        "event": "messageReceived"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    }
  ],
  "connections": {
    "Wati Trigger": {
      "main": [
        [
          {
            "node": "Command Router1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Command Router1": {
      "main": [
        [
          {
            "node": "Sheets \u2013 Find Vehicle for Booking1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sheets \u2013 Find Vehicle for Mileage1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sheets \u2013 Read Service History1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sheets \u2013 Read Vehicle for Status1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse Log Service1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sheets \u2013 Find Vehicle for Confirm1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "WATI \u2013 Send Help1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Service Due1": {
      "main": [
        [
          {
            "node": "Sheets \u2013 Mark Reminder Sent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Log Service1": {
      "main": [
        [
          {
            "node": "Sheets \u2013 Append Service Record1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build History View1": {
      "main": [
        [
          {
            "node": "WATI \u2013 Send History1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Vehicle Status1": {
      "main": [
        [
          {
            "node": "WATI \u2013 Send Vehicle Status1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Mileage Update1": {
      "main": [
        [
          {
            "node": "Sheets \u2013 Update Mileage1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Slot Confirmation1": {
      "main": [
        [
          {
            "node": "Sheets \u2013 Log Appointment1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Update Mileage1": {
      "main": [
        [
          {
            "node": "WATI \u2013 Send Mileage Ack1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Log Appointment1": {
      "main": [
        [
          {
            "node": "WATI \u2013 Confirm Appointment1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Tomorrow Appointments1": {
      "main": [
        [
          {
            "node": "WATI \u2013 Send Appointment Reminder1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Book & Generate Slots1": {
      "main": [
        [
          {
            "node": "WATI \u2013 Send Available Slots1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Read All Vehicles1": {
      "main": [
        [
          {
            "node": "Check Service Due1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Read Appointments1": {
      "main": [
        [
          {
            "node": "Check Tomorrow Appointments1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger \u2013 8AM Daily": {
      "main": [
        [
          {
            "node": "Sheets \u2013 Read Appointments1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Mark Reminder Sent1": {
      "main": [
        [
          {
            "node": "WATI \u2013 Send Service Reminder1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger \u2013 9AM Daily1": {
      "main": [
        [
          {
            "node": "Sheets \u2013 Read All Vehicles1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Read Service History1": {
      "main": [
        [
          {
            "node": "Build History View1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Append Service Record1": {
      "main": [
        [
          {
            "node": "WATI \u2013 Confirm Service Logged1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Read Vehicle for Status1": {
      "main": [
        [
          {
            "node": "Build Vehicle Status1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Find Vehicle for Booking1": {
      "main": [
        [
          {
            "node": "Parse Book & Generate Slots1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Find Vehicle for Confirm1": {
      "main": [
        [
          {
            "node": "Handle Slot Confirmation1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2013 Find Vehicle for Mileage1": {
      "main": [
        [
          {
            "node": "Parse Mileage Update1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Streamline your automotive service center's operations with this comprehensive automation. This workflow manages the entire customer lifecycle—from automated service reminders and instant appointment booking via WhatsApp to mileage tracking and full service history logs—all…

Source: https://n8n.io/workflows/13733/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

Elevate your shopping experience with an AI-driven personal assistant that lives right in your WhatsApp. This template automates the entire lifecycle of a shopping list—from intelligent intake and liv

HTTP Request, Google Sheets, N8N Nodes Wati
Slack & Telegram

Streamline your clinic's operations with a fully automated patient communication system. This workflow manages the entire appointment lifecycle—from automated morning reminders to real-time confirmati

Google Sheets, N8N Nodes Wati
Slack & Telegram

This workflow continuously monitors the TikTok Ads Library for new creatives from specific advertisers or keyword searches, scrapes them via Apify, logs them into Google Sheets, and sends concise noti

Google Sheets, Slack, Telegram +1
Slack & Telegram

This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.

Google Sheets, HTTP Request, Telegram
Slack & Telegram

Apollo Data Enrichment Using Company Id to automatically finds contacts for companies listed in your Google Sheet, enriches each person with emails and phone numbers via Apollo’s API, and writes verif

Google Sheets, HTTP Request, Error Trigger +1