AutomationFlowsWeb Scraping › Hourly Reminder Notifier via WhatsApp & Push

Hourly Reminder Notifier via WhatsApp & Push

Original n8n title: Reminder Notifier Wa+push (rest)

Reminder Notifier WA+Push (REST). Uses httpRequest. Scheduled trigger; 7 nodes.

Cron / scheduled trigger★★★★☆ complexity7 nodesHTTP Request
Web Scraping Trigger: Cron / scheduled Nodes: 7 Complexity: ★★★★☆ Added:

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
{
  "name": "Reminder Notifier WA+Push (REST)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 * * * *"
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "Schedule (Setiap Jam)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        240,
        300
      ],
      "notes": "Polling hourly. Untuk testing manual, klik 'Execute Workflow'."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.SUPABASE_URL }}/rest/v1/rpc/exec_sql",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.SUPABASE_SERVICE_ROLE_KEY }}"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.SUPABASE_SERVICE_ROLE_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=representation"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"query\": \"SELECT r.id, r.user_id, r.title, r.amount, r.category, r.notes, r.due_date, r.remind_days_before, r.recurrence, u.wa_number, u.nama FROM reminders r JOIN users u ON u.id = r.user_id WHERE r.status = 'pending' AND r.notify_via_wa = true AND (r.due_date - (r.remind_days_before * INTERVAL '1 day')) <= NOW() AND r.due_date >= (NOW() - INTERVAL '7 days') AND (r.reminded_at IS NULL OR r.reminded_at < NOW() - INTERVAL '23 hours') ORDER BY r.due_date ASC LIMIT 50\"\n}",
        "options": {}
      },
      "id": "fetch-due-reminders",
      "name": "Ambil Reminders Jatuh Tempo (Supabase)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        460,
        300
      ],
      "notes": "Catatan: butuh function exec_sql di Supabase ATAU ganti ke node Postgres langsung dengan koneksi DB. Lihat README.\n\nQuery: ambil reminder yang 'pending', perlu notif WA, sudah masuk window remind_days_before, dan belum dinotif dalam 23 jam terakhir."
    },
    {
      "parameters": {
        "fieldToSplitOut": "={{ $json }}",
        "options": {}
      },
      "id": "split-reminders",
      "name": "Split per Reminder",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Format pesan WhatsApp + push payload per reminder\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const r = item.json;\n  if (!r || !r.wa_number) continue;\n\n  const today = new Date();\n  today.setHours(0, 0, 0, 0);\n  const due = new Date(r.due_date);\n  due.setHours(0, 0, 0, 0);\n  const diffDays = Math.ceil((due - today) / (1000 * 60 * 60 * 24));\n\n  let dueText, pushSubject;\n  if (diffDays < 0) {\n    dueText = `\u26a0\ufe0f Sudah lewat ${Math.abs(diffDays)} hari`;\n    pushSubject = `Sudah lewat ${Math.abs(diffDays)} hari`;\n  } else if (diffDays === 0) {\n    dueText = '\ud83d\udd25 Hari ini!';\n    pushSubject = 'Jatuh tempo hari ini';\n  } else if (diffDays === 1) {\n    dueText = '\ud83d\udccc Besok';\n    pushSubject = 'Jatuh tempo besok';\n  } else {\n    dueText = `\ud83d\udcc5 ${diffDays} hari lagi`;\n    pushSubject = `${diffDays} hari lagi`;\n  }\n\n  const dueDateFormatted = due.toLocaleDateString('id-ID', {\n    day: 'numeric', month: 'long', year: 'numeric'\n  });\n\n  const namaSapaan = r.nama ? r.nama.split(' ')[0] : 'Kak';\n\n  let amountText = '';\n  let amountInline = '';\n  if (r.amount && r.amount > 0) {\n    const fmt = `Rp ${Number(r.amount).toLocaleString('id-ID')}`;\n    amountText = `\\n\ud83d\udcb0 *Estimasi:* ${fmt}`;\n    amountInline = ` (${fmt})`;\n  }\n\n  let categoryText = '';\n  if (r.category) categoryText = `\\n\ud83d\udcc2 *Kategori:* ${r.category}`;\n\n  let notesText = '';\n  if (r.notes) notesText = `\\n\ud83d\udcdd *Catatan:* ${r.notes}`;\n\n  const waMessage = `Halo ${namaSapaan}! \ud83d\udc4b\\n\\n` +\n    `Ada reminder buat kamu:\\n\\n` +\n    `*${r.title}*\\n` +\n    `${dueText} (${dueDateFormatted})` +\n    amountText +\n    categoryText +\n    notesText +\n    `\\n\\nKalau sudah dibayar, tandai di dashboard ya \ud83d\ude0a\\ndashboard.nayyiraai.online/reminders`;\n\n  let waFormatted = String(r.wa_number).replace(/\\D/g, '');\n  if (waFormatted.startsWith('0')) waFormatted = '62' + waFormatted.slice(1);\n  if (waFormatted.startsWith('8')) waFormatted = '62' + waFormatted;\n  const chatId = `${waFormatted}@s.whatsapp.net`;\n\n  const pushTitle = `\ud83d\udd14 ${r.title}`;\n  const pushBody = `${pushSubject}${amountInline}`;\n\n  results.push({\n    json: {\n      reminderId: r.id,\n      userId: r.user_id,\n      chatId: chatId,\n      message: waMessage,\n      title: r.title,\n      waNumber: waFormatted,\n      pushTitle: pushTitle,\n      pushBody: pushBody,\n    }\n  });\n}\n\nreturn results;"
      },
      "id": "format-message",
      "name": "Format Pesan WA + Push",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.WAHA_URL }}/api/sendText",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Api-Key",
              "value": "={{ $env.WAHA_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"session\": \"account1\",\n  \"chatId\": \"{{ $json.chatId }}\",\n  \"text\": {{ JSON.stringify($json.message) }}\n}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "send-wa",
      "name": "Kirim WhatsApp (WAHA)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        300
      ],
      "notes": "Pakai session 'account1' (sesuai tech spec). Ganti kalau session berbeda."
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{ $env.SUPABASE_URL }}/rest/v1/reminders?id=eq.{{ $('Format Pesan WA').item.json.reminderId }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.SUPABASE_SERVICE_ROLE_KEY }}"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.SUPABASE_SERVICE_ROLE_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=minimal"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"reminded_at\": \"{{ $now.toISOString() }}\"\n}",
        "options": {}
      },
      "id": "update-reminded-at",
      "name": "Update reminded_at (Supabase)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1340,
        300
      ],
      "notes": "Tandai bahwa reminder sudah dikirim notifikasi-nya, biar tidak spam dalam 23 jam ke depan."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.DASHBOARD_URL }}/api/push/send",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.INTERNAL_API_TOKEN }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"user_id\": \"{{ $json.userId }}\",\n  \"title\": {{ JSON.stringify($json.pushTitle) }},\n  \"body\": {{ JSON.stringify($json.pushBody) }},\n  \"url\": \"/reminders\",\n  \"tag\": \"reminder-{{ $json.reminderId }}\"\n}",
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "send-push",
      "name": "Kirim Push Notification",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        480
      ],
      "notes": "Kirim browser push. Auth: Bearer INTERNAL_API_TOKEN. neverError=true biar gagal push tidak menggagalkan workflow."
    }
  ],
  "connections": {
    "Schedule (Setiap Jam)": {
      "main": [
        [
          {
            "node": "Ambil Reminders Jatuh Tempo (Supabase)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ambil Reminders Jatuh Tempo (Supabase)": {
      "main": [
        [
          {
            "node": "Split per Reminder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split per Reminder": {
      "main": [
        [
          {
            "node": "Format Pesan WA + Push",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Pesan WA + Push": {
      "main": [
        [
          {
            "node": "Kirim WhatsApp (WAHA)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Kirim Push Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kirim WhatsApp (WAHA)": {
      "main": [
        [
          {
            "node": "Update reminded_at (Supabase)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataSuccessExecution": "all",
    "saveExecutionProgress": false,
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner"
  },
  "staticData": null,
  "versionId": "",
  "triggerCount": 0,
  "tags": [
    {
      "name": "nayyira"
    },
    {
      "name": "reminder"
    }
  ]
}
Pro

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

About this workflow

Reminder Notifier WA+Push (REST). Uses httpRequest. Scheduled trigger; 7 nodes.

Source: https://github.com/Hydraa57/nayyira-dashboard/blob/5ca4e7e6ceeb0f80a54ccc51ecc54c873ffbdc90/n8n-workflows/reminder_notifier.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

As n8n instances scale, teams often lose track of sub-workflows—who uses them, where they are referenced, and whether they can be safely updated. This leads to inefficiencies like unnecessary copies o

HTTP Request, n8n, N8N Trigger +1
Web Scraping

This workflow is an improvement of this workflow by Greg Brzezinka.

HTTP Request, Email Send, XML +1
Web Scraping

N8N-Workflow-Github-Manager. Uses github, httpRequest, n8n. Scheduled trigger; 38 nodes.

GitHub, HTTP Request, n8n
Web Scraping

This workflow uses KlickTipp community nodes, available for self-hosted n8n instances only.

N8N Nodes Klicktipp, Salesforce, Salesforce Trigger +1
Web Scraping

This workflow acts as an automated engagement bot. It sends a Direct Message (DM) with a link or resource to any follower who replies to your post with a specific target keyword.

HTTP Request