{
  "name": "Send multi-tenant reminders via Telegram from webhooks and schedule with Postgres logging",
  "nodes": [
    {
      "id": "9006229a-197a-46fd-b5b2-a1ad1abccd52",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        -16
      ],
      "parameters": {
        "width": 584,
        "height": 884,
        "content": "## Multi-tenant reminder system\n\n## How it works\nThis workflow has **3 entry points**:\n\n**1. Webhook trigger** — an external system sends an event (appointment, deadline, order) and the reminder fires"
      }
    },
    {
      "id": "4d459efd-cd19-45a7-a3ef-2aab53c3850a",
      "name": "Database Schema",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -16
      ],
      "parameters": {
        "width": 552,
        "height": 884,
        "content": "## Required database schema\n\n```sql\nCREATE TABLE tenants (\n  id SERIAL PRIMARY KEY,\n  name VARCHAR(255) NOT NULL,\n  channel VARCHAR(50) NOT NULL,\n  channel_config JSONB NOT NULL,\n  api_token VARCHAR(2"
      }
    },
    {
      "id": "67d9c4dc-0d45-4a3a-845a-8054ae568403",
      "name": "Webhook flow group",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        880
      ],
      "parameters": {
        "width": 1148,
        "height": 524,
        "content": "## Webhook flow\nReceives events from external systems.\nValidates the tenant token via `x-tenant-token` header,\nfetches tenant config from the database,\nand fires the reminder immediately."
      }
    },
    {
      "id": "7b2b0ff0-bcd0-4ce0-a5c7-3b982299f275",
      "name": "Schedule flow group",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        880
      ],
      "parameters": {
        "width": 212,
        "height": 260,
        "content": "## Schedule flow\nRuns every minute.\nQueries events within the configured timing window per tenant.\nIdempotency via `tenant_reminders_sent` — same reminder is never sent twice."
      }
    },
    {
      "id": "96a8d879-807a-48ec-8fe1-416b9d208cac",
      "name": "Registration group",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        1424
      ],
      "parameters": {
        "width": 1252,
        "height": 312,
        "content": "## Tenant registration\nBuilt-in n8n form to register new tenants.\nNo external backend needed.\nAccess at `/form/multi-tenant-register`"
      }
    },
    {
      "id": "48a46eb1-67d8-4d98-8cf0-16840e4d4a07",
      "name": "Send and log group",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1152,
        800
      ],
      "parameters": {
        "width": 760,
        "height": 604,
        "content": "## Send and log\nRoutes to the correct channel based on tenant config.\nLogs every attempt — success and error — to the database.\n\nTo add a new channel: duplicate the IF node\nand add the corresponding s"
      }
    },
    {
      "id": "e977f872-9060-4743-b7c1-0274d58bf25f",
      "name": "Every minute - check due events",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        544,
        1232
      ]
    },
    {
      "id": "81af22b5-b58e-4562-9208-c68844895011",
      "name": "Log success to database",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1664,
        1024
      ]
    },
    {
      "id": "4fa680a0-ec02-44a9-93ef-963a95de85f0",
      "name": "Mark reminder as sent",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1664,
        832
      ]
    },
    {
      "id": "a442acce-3f97-4e0d-bf8a-2e3b6de81ce3",
      "name": "Render Template",
      "type": "n8n-nodes-base.code",
      "position": [
        992,
        1136
      ]
    },
    {
      "id": "1703a6ab-1e8e-493d-919f-4d2c85f42be0",
      "name": "Log error to database",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1664,
        1232
      ]
    },
    {
      "id": "c8ef902b-34d9-4cf6-98fb-d7209efb25a9",
      "name": "Send reminder via Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1440,
        1024
      ]
    },
    {
      "id": "a961253c-5768-4223-bc3f-3a28bd902e67",
      "name": "Receive event via webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        96,
        1024
      ]
    },
    {
      "id": "6f926efe-a55f-463d-84cb-002d563c06c7",
      "name": "Validate tenant token",
      "type": "n8n-nodes-base.code",
      "position": [
        320,
        1024
      ]
    },
    {
      "id": "ee1e6760-a1d1-4dd6-9b3d-27be8ed30ab4",
      "name": "Map webhook fields",
      "type": "n8n-nodes-base.set",
      "position": [
        768,
        1024
      ]
    },
    {
      "id": "bd57f3b8-642a-4e5f-9bc4-6d12ab86f06f",
      "name": "Tenant registration form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        80,
        1552
      ]
    },
    {
      "id": "aea01fd8-2a93-4951-aaba-021588b14ef4",
      "name": "Organize form data",
      "type": "n8n-nodes-base.code",
      "position": [
        304,
        1552
      ]
    },
    {
      "id": "5b53a74a-858f-47fe-978e-684ebc260dbc",
      "name": "Show registration success",
      "type": "n8n-nodes-base.form",
      "position": [
        976,
        1552
      ]
    },
    {
      "id": "4aeb37bb-2fc3-4569-8393-b7af7d542af3",
      "name": "Fetch events due for reminder",
      "type": "n8n-nodes-base.postgres",
      "position": [
        768,
        1232
      ]
    },
    {
      "id": "7dcfdf57-bc38-47e3-aa19-95100c9e9426",
      "name": "Fetch tenant config",
      "type": "n8n-nodes-base.postgres",
      "position": [
        544,
        1024
      ]
    },
    {
      "id": "c45c91c4-585d-45b4-82c2-0fd47b52b283",
      "name": "Insert tenant rule",
      "type": "n8n-nodes-base.postgres",
      "position": [
        752,
        1552
      ]
    },
    {
      "id": "f1215f58-6250-43df-8188-207747655186",
      "name": "Insert tenant",
      "type": "n8n-nodes-base.postgres",
      "position": [
        528,
        1552
      ]
    },
    {
      "id": "d9402bd4-ea40-451a-a324-0d69abec2c09",
      "name": "No events due - skip",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1440,
        1232
      ]
    },
    {
      "id": "093748d1-c55f-4e6c-9be6-b767f6da243b",
      "name": "Route by channel type",
      "type": "n8n-nodes-base.if",
      "position": [
        1216,
        1136
      ]
    }
  ],
  "connections": {
    "Insert tenant": {
      "main": [
        [
          {
            "node": "Insert tenant rule",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Render Template": {
      "main": [
        [
          {
            "node": "Route by channel type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert tenant rule": {
      "main": [
        [
          {
            "node": "Show registration success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map webhook fields": {
      "main": [
        [
          {
            "node": "Render Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Organize form data": {
      "main": [
        [
          {
            "node": "Insert tenant",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch tenant config": {
      "main": [
        [
          {
            "node": "Map webhook fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by channel type": {
      "main": [
        [
          {
            "node": "Send reminder via Telegram",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No events due - skip",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate tenant token": {
      "main": [
        [
          {
            "node": "Fetch tenant config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tenant registration form": {
      "main": [
        [
          {
            "node": "Organize form data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive event via webhook": {
      "main": [
        [
          {
            "node": "Validate tenant token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send reminder via Telegram": {
      "main": [
        [
          {
            "node": "Mark reminder as sent",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log success to database",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log error to database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch events due for reminder": {
      "main": [
        [
          {
            "node": "Render Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every minute - check due events": {
      "main": [
        [
          {
            "node": "Fetch events due for reminder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}