Running a restaurant or hotel is a constant juggle of reservations, staff schedules, reviews, and end-of-day numbers. Most operators are still doing this manually — or paying $200+/month for specialized software that only half-works.
Here are 5 n8n automations that handle the busywork for free, with full workflow JSON you can import and run today.
1. Reservation Reminder System (Cut No-Shows by 60%)
No-shows kill restaurants. A well-timed reminder sequence cuts them 60-70%.
What it does:
- Checks your reservations spreadsheet every hour
- Sends a personalized email 24h before and 2h before each reservation
- Includes party size, time, and a "reply to cancel" prompt
Workflow (5 nodes): Schedule Trigger → Google Sheets (read reservations) → Code (filter 24h/2h windows) → IF (any pending?) → Gmail (send reminder)
{
"name": "Reservation Reminder System",
"nodes": [
{ "id": "1", "name": "Every Hour", "type": "n8n-nodes-base.scheduleTrigger",
"parameters": { "rule": { "interval": [{ "field": "hours", "hoursInterval": 1 }] } },
"position": [240, 300] },
{ "id": "2", "name": "Get Reservations", "type": "n8n-nodes-base.googleSheets",
"parameters": { "operation": "readRows", "documentId": "YOUR_SHEET_ID", "sheetName": "Reservations" },
"position": [460, 300] },
{ "id": "3", "name": "Filter 24h & 2h Windows", "type": "n8n-nodes-base.code",
"parameters": { "jsCode": "const now = Date.now();\nconst h24 = now + 24*3600*1000;\nconst h2 = now + 2*3600*1000;\nconst margin = 5*60*1000;\nreturn items.filter(item => {\n const t = new Date(item.json.reservation_time).getTime();\n return (Math.abs(t-h24)<margin || Math.abs(t-h2)<margin) && item.json.reminded !== 'yes';\n});" },
"position": [680, 300] },
{ "id": "4", "name": "Any to Remind?", "type": "n8n-nodes-base.if",
"parameters": { "conditions": { "number": [{ "value1": "={{ $items().length }}", "operation": "larger", "value2": 0 }] } },
"position": [900, 300] },
{ "id": "5", "name": "Send Reminder Email", "type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toList": "={{ $json.email }}",
"subject": "Reminder: Your reservation tonight at {{ $json.reservation_time }}",
"message": "Hi {{ $json.name }},\n\nJust a reminder — you have a reservation for {{ $json.party_size }} guests at {{ $json.reservation_time }}.\n\nIf plans change, simply reply to cancel — we really appreciate the heads-up.\n\nSee you soon!"
},
"position": [1120, 240] }
],
"connections": {
"Every Hour": { "main": [[{ "node": "Get Reservations", "type": "main", "index": 0 }]] },
"Get Reservations": { "main": [[{ "node": "Filter 24h & 2h Windows", "type": "main", "index": 0 }]] },
"Filter 24h & 2h Windows": { "main": [[{ "node": "Any to Remind?", "type": "main", "index": 0 }]] },
"Any to Remind?": { "main": [[{ "node": "Send Reminder Email", "type": "main", "index": 0 }], []] }
}
}
Setup (5 min): Import JSON → create Google Sheet with columns name, email, reservation_time, party_size, reminded → connect Gmail credentials → activate.
Pro tip: Add a Twilio SMS node in parallel with Gmail. SMS reminders have 95% open rates vs 30% for email.
2. Daily Revenue & Covers Dashboard (Delivered to Your Inbox Every Night)
Stop manually compiling end-of-day numbers. This workflow runs at 10 PM and sends your key metrics directly to you and your managers.
What it does:
- Pulls today's orders from your tracking spreadsheet (or POS API)
- Calculates covers, total revenue, average spend per cover, and top-selling items
- Sends an HTML report to your management team
Workflow (4 nodes): Schedule Trigger (10 PM) → Google Sheets → Code (aggregate KPIs) → Gmail (HTML email)
{
"name": "Daily Restaurant Revenue Report",
"nodes": [
{ "id": "1", "name": "10PM Every Night", "type": "n8n-nodes-base.scheduleTrigger",
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 22 * * *" }] } },
"position": [240, 300] },
{ "id": "2", "name": "Get Today Orders", "type": "n8n-nodes-base.googleSheets",
"parameters": { "operation": "readRows", "documentId": "YOUR_SHEET_ID", "sheetName": "Orders" },
"position": [460, 300] },
{ "id": "3", "name": "Calculate KPIs", "type": "n8n-nodes-base.code",
"parameters": { "jsCode": "const today = new Date().toISOString().split('T')[0];\nconst todayOrders = items.filter(i => i.json.date === today);\nconst revenue = todayOrders.reduce((s,i) => s + Number(i.json.amount), 0);\nconst covers = todayOrders.reduce((s,i) => s + Number(i.json.covers||1), 0);\nconst avgSpend = covers > 0 ? (revenue/covers).toFixed(2) : 0;\nconst itemCounts = {};\ntodayOrders.forEach(i => { itemCounts[i.json.item] = (itemCounts[i.json.item]||0)+1; });\nconst topItems = Object.entries(itemCounts).sort((a,b)=>b[1]-a[1]).slice(0,3).map(e=>e[0]).join(', ');\nreturn [{ json: { revenue: revenue.toFixed(2), covers, avgSpend, topItems, date: today } }];" },
"position": [680, 300] },
{ "id": "4", "name": "Email Report", "type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toList": "manager@yourrestaurant.com",
"subject": "Daily Report — {{ $json.date }}",
"message": "<h2>End of Day Summary</h2><table><tr><td><b>Date</b></td><td>{{ $json.date }}</td></tr><tr><td><b>Revenue</b></td><td>${{ $json.revenue }}</td></tr><tr><td><b>Covers</b></td><td>{{ $json.covers }}</td></tr><tr><td><b>Avg Spend/Cover</b></td><td>${{ $json.avgSpend }}</td></tr><tr><td><b>Top Items</b></td><td>{{ $json.topItems }}</td></tr></table>"
},
"position": [900, 300] }
],
"connections": {
"10PM Every Night": { "main": [[{ "node": "Get Today Orders", "type": "main", "index": 0 }]] },
"Get Today Orders": { "main": [[{ "node": "Calculate KPIs", "type": "main", "index": 0 }]] },
"Calculate KPIs": { "main": [[{ "node": "Email Report", "type": "main", "index": 0 }]] }
}
}
Pro tip: Connect to Square, Toast, or Lightspeed APIs via HTTP Request instead of Google Sheets for real-time POS data.
3. Google Reviews Monitor & Alert (Respond in Under 2 Hours)
Responding to negative reviews within 2 hours is one of the highest-ROI things a restaurant can do. This automation makes it impossible to miss a bad review.
What it does:
- Checks your Google Places listing daily via the Places API
- Sends an instant Slack alert for any review 3 stars or under
- Sends a Slack notification for 4-5 star reviews so you can say thank you
Workflow (5 nodes): Schedule Trigger → HTTP Request (Google Places API) → Code (filter new reviews, classify by rating) → Switch → Slack alert
{
"name": "Google Reviews Monitor",
"nodes": [
{ "id": "1", "name": "Every Morning 9AM", "type": "n8n-nodes-base.scheduleTrigger",
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 9 * * *" }] } },
"position": [240, 300] },
{ "id": "2", "name": "Fetch Reviews", "type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://maps.googleapis.com/maps/api/place/details/json",
"method": "GET",
"queryParameters": { "parameters": [
{ "name": "place_id", "value": "YOUR_PLACE_ID" },
{ "name": "fields", "value": "reviews" },
{ "name": "key", "value": "YOUR_GOOGLE_API_KEY" }
]}
},
"position": [460, 300] },
{ "id": "3", "name": "Filter New Reviews", "type": "n8n-nodes-base.code",
"parameters": { "jsCode": "const reviews = $json.result?.reviews || [];\nconst yesterday = Date.now() - 86400000;\nreturn reviews\n .filter(r => (r.time * 1000) > yesterday)\n .map(r => ({ json: { rating: r.rating, text: r.text, author: r.author_name, type: r.rating <= 3 ? 'negative' : 'positive' } }));" },
"position": [680, 300] },
{ "id": "4", "name": "Route by Rating", "type": "n8n-nodes-base.switch",
"parameters": { "rules": { "rules": [
{ "conditions": { "string": [{ "value1": "={{ $json.type }}", "operation": "equal", "value2": "negative" }] }, "outputKey": "0" },
{ "conditions": { "string": [{ "value1": "={{ $json.type }}", "operation": "equal", "value2": "positive" }] }, "outputKey": "1" }
]}},
"position": [900, 300] },
{ "id": "5", "name": "Alert — Low Rating", "type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#reviews",
"text": "New {{ $json.rating }}-star review from {{ $json.author }}:\n\n\"{{ $json.text }}\"\n\nRespond on Google My Business immediately."
},
"position": [1120, 200] }
],
"connections": {
"Every Morning 9AM": { "main": [[{ "node": "Fetch Reviews", "type": "main", "index": 0 }]] },
"Fetch Reviews": { "main": [[{ "node": "Filter New Reviews", "type": "main", "index": 0 }]] },
"Filter New Reviews": { "main": [[{ "node": "Route by Rating", "type": "main", "index": 0 }]] },
"Route by Rating": { "main": [[{ "node": "Alert — Low Rating", "type": "main", "index": 0 }], []] }
}
}
4. Staff Schedule Change Notifier
When someone calls in sick or requests a swap, the scramble to notify everyone manually takes 20-30 minutes. This automation handles it in seconds.
What it does:
- Watches your schedule spreadsheet for any cell changes
- Immediately emails the affected staff member with shift details
- Posts a Slack alert to your team's #schedule channel
Workflow (4 nodes): Google Sheets Trigger → Code (extract shift details) → Gmail (notify staff) + Slack (alert team)
{
"name": "Staff Schedule Change Notifier",
"nodes": [
{ "id": "1", "name": "Schedule Sheet Changed", "type": "n8n-nodes-base.googleSheetsTrigger",
"parameters": { "documentId": "YOUR_SCHEDULE_SHEET_ID", "sheetName": "Shifts", "triggerOn": "anyChange" },
"position": [240, 300] },
{ "id": "2", "name": "Extract Details", "type": "n8n-nodes-base.code",
"parameters": { "jsCode": "const row = $json;\nreturn [{ json: { date: row.shift_date, time: row.shift_time, station: row.station, name: row.staff_name, email: row.staff_email, status: row.status, notes: row.notes||'' } }];" },
"position": [460, 300] },
{ "id": "3", "name": "Email Staff", "type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toList": "={{ $json.email }}",
"subject": "Shift Update — {{ $json.date }} {{ $json.time }}",
"message": "Hi {{ $json.name }},\n\nYour {{ $json.date }} shift at {{ $json.time }} ({{ $json.station }}) has been updated.\nStatus: {{ $json.status }}\n{{ $json.notes ? 'Notes: ' + $json.notes : '' }}"
},
"position": [680, 200] },
{ "id": "4", "name": "Slack Team Alert", "type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#schedule",
"text": "Schedule update: {{ $json.name }}'s {{ $json.date }} shift ({{ $json.time }}, {{ $json.station }}) is now {{ $json.status }}."
},
"position": [680, 400] }
],
"connections": {
"Schedule Sheet Changed": { "main": [[{ "node": "Extract Details", "type": "main", "index": 0 }]] },
"Extract Details": { "main": [[{ "node": "Email Staff", "type": "main", "index": 0 }, { "node": "Slack Team Alert", "type": "main", "index": 0 }]] }
}
}
5. Customer Win-Back Campaign ("We Miss You" Sequence)
Repeat customers spend 67% more than new ones. This automation identifies lapsed guests and sends a personalized offer every Monday morning.
What it does:
- Scans your customer database weekly
- Finds guests who haven't visited in 30+ days and haven't received a win-back email
- Sends a personalized email with a discount code
Workflow (4 nodes): Schedule Trigger (Monday 9 AM) → Google Sheets → Code (filter 30+ days inactive) → Gmail (personalized win-back)
{
"name": "Customer Win-Back Campaign",
"nodes": [
{ "id": "1", "name": "Monday 9AM", "type": "n8n-nodes-base.scheduleTrigger",
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 9 * * 1" }] } },
"position": [240, 300] },
{ "id": "2", "name": "Get Customer List", "type": "n8n-nodes-base.googleSheets",
"parameters": { "operation": "readRows", "documentId": "YOUR_SHEET_ID", "sheetName": "Customers" },
"position": [460, 300] },
{ "id": "3", "name": "Find Lapsed Guests", "type": "n8n-nodes-base.code",
"parameters": { "jsCode": "const threshold = Date.now() - 30*86400000;\nreturn items.filter(i => {\n const lastVisit = new Date(i.json.last_visit).getTime();\n return lastVisit < threshold && i.json.winback_sent !== 'yes';\n});" },
"position": [680, 300] },
{ "id": "4", "name": "Win-Back Email", "type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toList": "={{ $json.email }}",
"subject": "We miss you, {{ $json.first_name }} — here's 15% off",
"message": "Hi {{ $json.first_name }},\n\nIt's been a while since we've seen you, and we wanted to reach out.\n\nUse code WELCOMEBACK for 15% off your next visit — valid for the next 30 days.\n\nHope to see you soon!"
},
"position": [900, 300] }
],
"connections": {
"Monday 9AM": { "main": [[{ "node": "Get Customer List", "type": "main", "index": 0 }]] },
"Get Customer List": { "main": [[{ "node": "Find Lapsed Guests", "type": "main", "index": 0 }]] },
"Find Lapsed Guests": { "main": [[{ "node": "Win-Back Email", "type": "main", "index": 0 }]] }
}
}
Pro tip: Track redemption by adding winback_sent: yes + winback_code columns to your sheet after each send. After a month you'll know your exact win-back ROI.
All 5 Workflows at a Glance
| Workflow | Nodes | Setup | Impact |
|---|---|---|---|
| Reservation Reminder | 5 | 5 min | 60% fewer no-shows |
| Daily Revenue Report | 4 | 10 min | 30 min/day saved |
| Google Review Monitor | 5 | 15 min | Respond in < 2 hours |
| Staff Schedule Alert | 4 | 5 min | 20 min per shift change saved |
| Customer Win-Back | 4 | 10 min | 5-15% repeat visit lift |
Total setup time: ~45 minutes. Typical time saved: 4-6 hours per week.
Want Ready-to-Import Packages with Full Documentation?
If you'd rather skip the setup and use production-ready versions with error handling, retry logic, and step-by-step guides already included — check out FlowKit at stripeai.gumroad.com.
The Appointment Reminder ($15) and Daily Report Generator ($19) templates map directly to the restaurant use cases above. There's also a Complete Bundle with all 15 templates for $97 — less than a single month of most hospitality SaaS tools.
Which of these would save you the most time? Drop it in the comments — I read every one.
Top comments (0)