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 →
{
"name": "Appointment Reminder & No-Show Rescue Engine",
"active": false,
"settings": {
"executionOrder": "v1",
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all",
"saveManualExecutions": true
},
"nodes": [
{
"name": "Run Sweep Now",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-1180,
280
],
"parameters": {}
},
{
"name": "Every 15 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-1180,
460
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 15
}
]
}
}
},
{
"name": "Init Reminder Run",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-960,
370
],
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": {
"$code": "code/01-init-reminder-run.js"
}
}
},
{
"name": "Use Live Calendar?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-740,
370
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "use-live",
"leftValue": "={{ $json.useLive }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"looseTypeValidation": true,
"options": {}
}
},
{
"name": "CRM: Fetch Appointments",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-500,
240
],
"parameters": {
"method": "GET",
"url": "https://services.leadconnectorhq.com/calendars/events",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendQuery": true,
"specifyQuery": "keypair",
"queryParameters": {
"parameters": [
{
"name": "locationId",
"value": "={{ $('Init Reminder Run').item.json.config.locationId }}"
},
{
"name": "startTime",
"value": "={{ new Date(Date.now() - 5 * 86400000).toISOString() }}"
},
{
"name": "endTime",
"value": "={{ new Date(Date.now() + 3 * 86400000).toISOString() }}"
}
]
},
"sendHeaders": true,
"specifyHeaders": "keypair",
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-04-15"
},
{
"name": "Accept",
"value": "application/json"
}
]
},
"options": {
"timeout": 20000,
"response": {
"response": {
"fullResponse": true,
"neverError": true
}
}
}
}
},
{
"name": "Mock Appointments",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-500,
500
],
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": {
"$code": "code/02-mock-appointments.js"
}
}
},
{
"name": "Classify Fetch Error",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-260,
40
],
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": {
"$code": "code/05-classify-api-error.js"
}
}
},
{
"name": "Retry Fetch?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-40,
40
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "should-retry",
"leftValue": "={{ $json.shouldRetry }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"looseTypeValidation": true,
"options": {}
}
},
{
"name": "Backoff Wait",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
-40,
-160
],
"parameters": {
"amount": "={{ $json.retry.waitSeconds }}",
"unit": "seconds"
}
},
{
"name": "Stop: Calendar Unavailable",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
200,
-80
],
"parameters": {}
},
{
"name": "Plan Sends",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-40,
370
],
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": {
"$code": "code/03-plan-sends.js"
}
}
},
{
"name": "Anything Due?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
180,
370
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "has-sends",
"leftValue": "={{ $json.hasSends }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"looseTypeValidation": true,
"options": {}
}
},
{
"name": "Nothing Due This Sweep",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
420,
520
],
"parameters": {}
},
{
"name": "Render Messages",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
420,
280
],
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": {
"$code": "code/04-render-message.js"
}
}
},
{
"name": "Send For Real?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
640,
280
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "really-send",
"leftValue": "={{ $json.config.simulateDelivery !== true && $json.blocked !== true }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"looseTypeValidation": true,
"options": {}
}
},
{
"name": "Simulate Delivery",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
880,
460
],
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": {
"$code": "code/06-simulate-delivery.js"
}
}
},
{
"name": "SMS or Email?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
880,
160
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "is-sms",
"leftValue": "={{ $json.channel === 'sms' }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"looseTypeValidation": true,
"options": {}
}
},
{
"name": "Send SMS",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1120,
60
],
"parameters": {
"method": "POST",
"url": "https://services.leadconnectorhq.com/conversations/messages",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"specifyHeaders": "keypair",
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-04-15"
},
{
"name": "Accept",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ type: 'SMS', contactId: $json.contactId, message: $json.body }) }}",
"options": {
"timeout": 20000,
"batching": {
"batch": {
"batchSize": 10,
"batchInterval": 1000
}
},
"response": {
"response": {
"fullResponse": true,
"neverError": true
}
}
}
}
},
{
"name": "Send Email",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1120,
260
],
"parameters": {
"method": "POST",
"url": "https://services.leadconnectorhq.com/conversations/messages",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"specifyHeaders": "keypair",
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-04-15"
},
{
"name": "Accept",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ type: 'Email', contactId: $json.contactId, subject: $json.subject, message: $json.body }) }}",
"options": {
"timeout": 20000,
"batching": {
"batch": {
"batchSize": 10,
"batchInterval": 1000
}
},
"response": {
"response": {
"fullResponse": true,
"neverError": true
}
}
}
}
},
{
"name": "Record Sends",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1360,
260
],
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": {
"$code": "code/07-record-sends.js"
}
}
},
{
"name": "README",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1200,
-60
],
"parameters": {
"width": 460,
"height": 280,
"color": 4,
"content": "## Appointment reminders that respect the customer's clock\n\nRuns every 15 minutes. Works out which reminder touches are due, in the customer's own timezone, defers anything landing in quiet hours, and cancels a no-show sequence the moment the customer rebooks.\n\n**Sends nothing by default.** `simulateDelivery: true` and `mockMode: true` in **Init Reminder Run**. Hit **Test workflow** and read **Record Sends**.\n\nWhile `mockMode` is on the sweep is evaluated at `mockClockIso`, not at *now* \u2014 the fixture calendar only means anything relative to one instant, so pinning the clock is what makes this demo show the same sweep on any day. The live path always uses the real clock.\n\nThe node to open is **Plan Sends**."
}
},
{
"name": "Why this is code",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-280,
-300
],
"parameters": {
"width": 480,
"height": 240,
"color": 5,
"content": "## The three things a reminder builder gets wrong\n\n**Timezones.** \"Send at 9am\" means 9am where the customer is. Fixed offsets break twice a year; the server's clock texts Sydney at 3am. This uses Intl with real IANA zones, so DST is the tz database's problem, not ours.\n\n**Idempotency.** A 15-minute sweep with a 90-minute due window sees every touch six times. A deterministic key per (appointment, touch, channel) is what stops six texts.\n\n**Cancellation.** A rescue sequence that keeps running after the customer rebooks is worse than no sequence."
}
},
{
"name": "SMS billing",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
180,
-80
],
"parameters": {
"width": 400,
"height": 200,
"color": 3,
"content": "## Segment counting\n\nSMS is billed per segment. One emoji in a service name flips the message from GSM-7 to UCS-2 and drops the segment size from 160 characters to 70 \u2014 so a 150-character message silently bills as 3 segments instead of 1.\n\n**Render Messages** counts this before sending and blocks anything over the configured limit. It also refuses to send a message with an unresolved `{{placeholder}}`."
}
},
{
"name": "Calendar Fetched OK?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-260,
240
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "calendar-fetched-ok",
"leftValue": "={{ $json.statusCode >= 200 && $json.statusCode < 300 }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"looseTypeValidation": true,
"options": {}
}
}
],
"connections": {
"Run Sweep Now": {
"main": [
[
{
"node": "Init Reminder Run",
"type": "main",
"index": 0
}
]
]
},
"Every 15 Minutes": {
"main": [
[
{
"node": "Init Reminder Run",
"type": "main",
"index": 0
}
]
]
},
"Init Reminder Run": {
"main": [
[
{
"node": "Use Live Calendar?",
"type": "main",
"index": 0
}
]
]
},
"Use Live Calendar?": {
"main": [
[
{
"node": "CRM: Fetch Appointments",
"type": "main",
"index": 0
}
],
[
{
"node": "Mock Appointments",
"type": "main",
"index": 0
}
]
]
},
"CRM: Fetch Appointments": {
"main": [
[
{
"node": "Calendar Fetched OK?",
"type": "main",
"index": 0
}
]
]
},
"Mock Appointments": {
"main": [
[
{
"node": "Plan Sends",
"type": "main",
"index": 0
}
]
]
},
"Classify Fetch Error": {
"main": [
[
{
"node": "Retry Fetch?",
"type": "main",
"index": 0
}
]
]
},
"Retry Fetch?": {
"main": [
[
{
"node": "Backoff Wait",
"type": "main",
"index": 0
}
],
[
{
"node": "Stop: Calendar Unavailable",
"type": "main",
"index": 0
}
]
]
},
"Backoff Wait": {
"main": [
[
{
"node": "CRM: Fetch Appointments",
"type": "main",
"index": 0
}
]
]
},
"Plan Sends": {
"main": [
[
{
"node": "Anything Due?",
"type": "main",
"index": 0
}
]
]
},
"Anything Due?": {
"main": [
[
{
"node": "Render Messages",
"type": "main",
"index": 0
}
],
[
{
"node": "Nothing Due This Sweep",
"type": "main",
"index": 0
}
]
]
},
"Render Messages": {
"main": [
[
{
"node": "Send For Real?",
"type": "main",
"index": 0
}
]
]
},
"Send For Real?": {
"main": [
[
{
"node": "SMS or Email?",
"type": "main",
"index": 0
}
],
[
{
"node": "Simulate Delivery",
"type": "main",
"index": 0
}
]
]
},
"SMS or Email?": {
"main": [
[
{
"node": "Send SMS",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Email",
"type": "main",
"index": 0
}
]
]
},
"Send SMS": {
"main": [
[
{
"node": "Record Sends",
"type": "main",
"index": 0
}
]
]
},
"Send Email": {
"main": [
[
{
"node": "Record Sends",
"type": "main",
"index": 0
}
]
]
},
"Simulate Delivery": {
"main": [
[
{
"node": "Record Sends",
"type": "main",
"index": 0
}
]
]
},
"Calendar Fetched OK?": {
"main": [
[
{
"node": "Plan Sends",
"type": "main",
"index": 0
}
],
[
{
"node": "Classify Fetch Error",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Appointment Reminder & No-Show Rescue Engine. Uses httpRequest. Event-driven trigger; 24 nodes.
Source: https://github.com/ihthicodes/n8n-agency-demos/blob/main/demos/03-appointment-reminder-engine/workflow.src.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow listens for an “Approved” label on a Trello card, reads the AI draft bookkeeping JSON from card comments, and posts the corresponding transaction to Xero. It then adds a Xero deep link b
02_LLM_Pipeline v1.0. Uses executeWorkflowTrigger, httpRequest, seaTable. Event-driven trigger; 65 nodes.
This workflow allows you to import any workflow from a file or another n8n instance and map the credentials easily. A multi-form setup guides you through the entire process At the beginning you have t
[n8n] Advanced URL Parsing and Shortening Workflow - Switchy.io Integration. Uses splitInBatches, stickyNote, httpRequest, html. Event-driven trigger; 56 nodes.
[](https://youtu.be/c7yCZhmMjtI)