{
  "meta": {
    "instanceId": "moltflow-template",
    "templateCredsSetupCompleted": true
  },
  "name": "Send WhatsApp Appointment Reminders from Google Calendar with MoltFlow",
  "tags": [
    {
      "name": "whatsapp"
    },
    {
      "name": "google calendar"
    },
    {
      "name": "appointments"
    },
    {
      "name": "moltflow"
    },
    {
      "name": "reminders"
    }
  ],
  "nodes": [
    {
      "id": "h1000001-0000-4000-0800-000000000010",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        -300
      ],
      "parameters": {
        "color": 4,
        "width": 400,
        "height": 300,
        "content": "## Calendar → WhatsApp Reminders\nAutomatically send WhatsApp reminders for upcoming appointments from your Google Calendar using [MoltFlow](https://molt.waiflow.app).\n\n**How it works:**\n1. Runs every hour and checks your Google Calendar\n2. Finds appointments happening in the next 24 hours\n3. Extracts client phone numbers from event descriptions\n4. Sends a personalized WhatsApp reminder via MoltFlow"
      },
      "typeVersion": 1
    },
    {
      "id": "h1000001-0000-4000-0800-000000000011",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        420,
        -300
      ],
      "parameters": {
        "color": 5,
        "width": 400,
        "height": 270,
        "content": "## Setup (5 min)\n1. Create a [MoltFlow account](https://molt.waiflow.app) and connect WhatsApp\n2. Connect Google Calendar OAuth2 credential\n3. Set your calendar ID (default: primary)\n4. Set `YOUR_SESSION_ID` in the Format Reminders node\n5. Add MoltFlow API Key: Header Auth → `X-API-Key`\n\n**Format:** Add client phone to calendar event description:\n`Phone: 1234567890`"
      },
      "typeVersion": 1
    },
    {
      "id": "h1000001-0000-4000-0800-000000000001",
      "name": "Every Hour",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 1
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "h1000001-0000-4000-0800-000000000002",
      "name": "Get Upcoming Events",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        220,
        0
      ],
      "parameters": {
        "options": {
          "timeMax": "={{ $now.plus({ hours: 24 }).toISO() }}",
          "timeMin": "={{ $now.toISO() }}",
          "singleEvents": true
        },
        "operation": "getAll",
        "returnAll": true,
        "calendarId": {
          "__rl": true,
          "mode": "list",
          "value": "primary"
        }
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "id": "",
          "name": "Google Calendar"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "h1000001-0000-4000-0800-000000000003",
      "name": "Format Reminders",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const SESSION_ID = 'YOUR_SESSION_ID';\n\nconst events = $input.all();\nconst reminders = [];\n\nfor (const event of events) {\n  const data = event.json;\n  const summary = data.summary || 'Appointment';\n  const description = data.description || '';\n  const startTime = data.start && (data.start.dateTime || data.start.date) || '';\n\n  const phoneMatch = description.match(/(?:phone|tel|mobile|whatsapp)[:\\\\s]*([+]?[0-9\\\\s-]{7,20})/i)\n    || description.match(/([+]?[0-9]{7,15})/);\n\n  if (!phoneMatch) continue;\n\n  const phone = phoneMatch[1].replace(/[^0-9]/g, '');\n  if (phone.length < 7) continue;\n\n  const nameMatch = description.match(/(?:name|client|patient)[:\\\\s]*([^\\\\n]+)/i);\n  const clientName = nameMatch\n    ? nameMatch[1].trim()\n    : (data.attendees && data.attendees.length > 0\n      ? data.attendees[0].displayName || data.attendees[0].email\n      : 'there');\n\n  let timeStr = startTime;\n  try {\n    const d = new Date(startTime);\n    timeStr = d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true });\n  } catch(e) {}\n\n  const message = `Hi ${clientName}! Just a reminder about your appointment: ${summary} tomorrow at ${timeStr}. Reply YES to confirm or let us know if you need to reschedule.`;\n\n  reminders.push({\n    json: {\n      session_id: SESSION_ID,\n      chat_id: phone + '@c.us',\n      message: message,\n      client_name: clientName,\n      event_name: summary,\n      event_time: startTime\n    }\n  });\n}\n\nif (reminders.length === 0) {\n  return [{ json: { status: 'no_reminders', message: 'No upcoming appointments with phone numbers found' } }];\n}\n\nreturn reminders;"
      },
      "typeVersion": 2
    },
    {
      "id": "h1000001-0000-4000-0800-000000000004",
      "name": "Has Reminder?",
      "type": "n8n-nodes-base.if",
      "position": [
        660,
        0
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-chat-id",
              "operator": {
                "type": "string",
                "operation": "exists"
              },
              "leftValue": "={{ $json.chat_id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "h1000001-0000-4000-0800-000000000005",
      "name": "Send WhatsApp Reminder",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        -100
      ],
      "parameters": {
        "url": "https://apiv2.waiflow.app/api/v2/messages/send",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ session_id: $json.session_id, chat_id: $json.chat_id, message: $json.message }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "",
          "name": "MoltFlow API Key"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "h1000001-0000-4000-0800-000000000006",
      "name": "Log Results",
      "type": "n8n-nodes-base.code",
      "position": [
        1100,
        -100
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const items = $input.all();\nconst results = items.map(item => ({\n  status: 'sent',\n  client: item.json.client_name || $('Format Reminders').first().json.client_name,\n  event: $('Format Reminders').first().json.event_name\n}));\nreturn [{ json: { summary: 'Sent ' + results.length + ' appointment reminder(s)', results } }];"
      },
      "typeVersion": 2
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Every Hour": {
      "main": [
        [
          {
            "node": "Get Upcoming Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Reminder?": {
      "main": [
        [
          {
            "node": "Send WhatsApp Reminder",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Format Reminders": {
      "main": [
        [
          {
            "node": "Has Reminder?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Upcoming Events": {
      "main": [
        [
          {
            "node": "Format Reminders",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send WhatsApp Reminder": {
      "main": [
        [
          {
            "node": "Log Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}