{
  "id": "JZ8C3IFRtHrntshw",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Subscription Renewal Reminder \u2013 Telegram & Supabase",
  "tags": [],
  "nodes": [
    {
      "id": "c27c9a31-87c7-45c4-91d6-f76c1f5e459f",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2032,
        -32
      ],
      "parameters": {
        "path": "subscription-renewal",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "e4709aa5-1ee7-4c7a-8cd9-d27071ce208d",
      "name": "Extract API Key",
      "type": "n8n-nodes-base.set",
      "position": [
        -1840,
        -32
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "0c98d8f4-dc5b-4ef9-898e-0102d9e0df35",
      "name": "Authorized?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1632,
        -32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.apiKey }}",
              "value2": "{{YOUR_SECRET_KEY}}",
              "operation": "equals"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "6b4ceea6-b888-4cfe-874d-0995182a6b20",
      "name": "Fetch Subscriptions",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -1440,
        -32
      ],
      "parameters": {
        "operation": "list"
      },
      "typeVersion": 1
    },
    {
      "id": "f5638554-9802-49a5-9947-06ee14dec899",
      "name": "Calculate Days",
      "type": "n8n-nodes-base.code",
      "position": [
        -1232,
        -32
      ],
      "parameters": {
        "jsCode": "// Add daysToExpiry to every row\nconst now = new Date();\nreturn $input.all().map(item => {\n  const expiry = new Date(item.json.expiry_date);\n  const diff = Math.ceil((expiry - now) / (1000*60*60*24));\n  return {\n    json: {\n      ...item.json,\n      daysToExpiry: diff\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "ef038b64-933a-48bc-97b7-dedfa0a2dab7",
      "name": "Expiring Soon?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1040,
        -32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "number": [
            {
              "value1": "={{ $json.daysToExpiry }}",
              "value2": 7,
              "operation": "smallerEqual"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3136b9a3-68de-4fc3-af9f-fac13d69b8aa",
      "name": "Create Telegram Message",
      "type": "n8n-nodes-base.set",
      "position": [
        -832,
        -32
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "e52d93f6-d14c-42e6-ae9b-a332176a4fac",
      "name": "Send Telegram Reminder",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -640,
        -32
      ],
      "parameters": {
        "text": "={{ $json.telegramMessage }}",
        "chatId": "={{ $json.chatId }}",
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "fdd0c495-c9b6-4d59-a79a-fb6dd99a84d7",
      "name": "Telegram Sent?",
      "type": "n8n-nodes-base.if",
      "position": [
        -432,
        -32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.ok === true }}",
              "operation": "isTrue"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "7317ad39-4685-472a-b56e-4d512882717f",
      "name": "Prepare Reminder Log",
      "type": "n8n-nodes-base.set",
      "position": [
        -240,
        -32
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "a37b7f6f-fcf2-47b7-bc7b-d24bb166ad7a",
      "name": "Insert Reminder Log",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -32,
        -32
      ],
      "parameters": {
        "tableId": "renewal_reminders",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "customer_id",
              "fieldValue": "={{ $json.customer_id }}"
            },
            {
              "fieldId": "reminder_sent_at",
              "fieldValue": "={{ $json.reminder_sent_at }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "dbddf411-e753-470c-8160-9e49b49e3408",
      "name": "Compose Success Response",
      "type": "n8n-nodes-base.set",
      "position": [
        176,
        -32
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "f579f71e-d319-4661-9541-a41e01084ef6",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        368,
        -32
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "049b1ab5-006e-4690-9265-bc56cf0c40b8",
      "name": "Compose Error Response",
      "type": "n8n-nodes-base.set",
      "position": [
        -432,
        176
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "945303cd-c6a3-4e57-a712-bb973f2d7940",
      "name": "Insert Error Log",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -240,
        176
      ],
      "parameters": {
        "tableId": "workflow_errors",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "error_message",
              "fieldValue": "={{ $json.message }}"
            },
            {
              "fieldId": "error_detail",
              "fieldValue": "={{ $json.error_detail }}"
            },
            {
              "fieldId": "created_at",
              "fieldValue": "={{ new Date().toISOString() }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "29619ad1-576d-4a3e-9451-e3b0b89c9b61",
      "name": "\ud83d\udd14 Subscription Renewal Reminder \u2013 Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2736,
        -368
      ],
      "parameters": {
        "width": 550,
        "height": 738,
        "content": "## How it works\nThis workflow listens for an incoming webhook call to kick off a subscription-renewal check. After validating an API key, it queries a Supabase table that stores all active subscriptions. Each record is enriched with a calculated **daysToExpiry** value, then filtered so only customers whose plan expires in seven days or fewer proceed. For every imminent renewal, a friendly Telegram reminder is sent. Successful sends are logged back to Supabase while failures or unauthorized calls create an error entry. Finally, the workflow returns a concise JSON response to the original webhook request.\n\n## Setup steps\n1. Add your Supabase project URL and service role key to an **Supabase API** credential.\n2. Create two tables: `subscriptions` (with columns: customer_id, customer_name, telegram_chat_id, expiry_date) and `renewal_reminders`.\n3. Generate a Telegram bot and store its token in a **Telegram API** credential.\n4. Replace `{{YOUR_SECRET_KEY}}` in the *Authorized?* IF node with a secure value and pass it via an `x-api-key` header when calling the webhook.\n5. Deploy the workflow and copy the production webhook URL from the *Webhook Trigger* node.\n6. Schedule or call the webhook from your billing system on a daily basis.\n7. Monitor Supabase tables for reminder logs and review Telegram delivery results."
      },
      "typeVersion": 1
    },
    {
      "id": "99d33e26-9974-440c-82c7-1980a86208a1",
      "name": "\ud83d\udee1\ufe0f Trigger & Auth (Info)",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1984,
        -400
      ],
      "parameters": {
        "color": 7,
        "width": 610,
        "height": 702,
        "content": "## Trigger & Authentication\nThis block handles all incoming requests. The **Webhook Trigger** node exposes a public endpoint that external services can call. Immediately after triggering, the workflow extracts the `x-api-key` header so it can be validated. The **Authorized?** IF node compares the provided key with your secret. Unauthorized traffic is short-circuited, returning an error JSON and recording the event in the error-logging branch. Keeping authentication logic up front prevents unnecessary Supabase queries and secures the rest of the workflow.\n\nKey points:\n\u2022 Webhook response mode is set to *Response Node* so we can craft custom replies.\n\u2022 API key travels in headers, avoiding exposure in query strings.\n\u2022 All failed checks are directed to the error-handling lane for visibility."
      },
      "typeVersion": 1
    },
    {
      "id": "b44c28e9-84d1-4786-aa5d-92a1592c913a",
      "name": "\ud83d\udcca Processing (Info)",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1296,
        -368
      ],
      "parameters": {
        "color": 7,
        "width": 738,
        "height": 654,
        "content": "## Processing & Filtering\nOnce a request is authorized, the workflow pulls current subscription rows from Supabase. The **Calculate Days** code snippet computes the remaining time until each subscription\u2019s `expiry_date`. Next, the **Expiring Soon?** IF node isolates customers whose plans lapse within seven days. This logic keeps Telegram traffic efficient, ensuring only relevant users receive messages. Because n8n processes items individually, each record moves independently through the remainder of the flow, enabling fine-grained logging and error handling without complex loops."
      },
      "typeVersion": 1
    },
    {
      "id": "d60acb9e-730e-44d6-a347-f2e4761a0a59",
      "name": "\ud83d\udce8 Notification & Logging (Info)",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -496,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 1010,
        "height": 638,
        "content": "## Notifications & Logging\nFor every imminent renewal, a personalized message is assembled and dispatched via the **Send Telegram Reminder** node. Successful sends trigger a logging sequence that writes a confirmation row into `renewal_reminders`. If Telegram responds with an error (e.g., invalid chat ID), the alternative branch builds an error object and stores it in `workflow_errors`. Both success and failure paths conclude by assembling a human-readable JSON payload that the **Respond to Webhook** node returns to the caller, closing the loop with clear status feedback."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "5b7cced7-f575-4ed7-8cfe-ab88ae5a994d",
  "connections": {
    "Authorized?": {
      "main": [
        [
          {
            "node": "Fetch Subscriptions",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Compose Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Days": {
      "main": [
        [
          {
            "node": "Expiring Soon?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Expiring Soon?": {
      "main": [
        [
          {
            "node": "Create Telegram Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Compose Success Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Sent?": {
      "main": [
        [
          {
            "node": "Prepare Reminder Log",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Compose Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract API Key": {
      "main": [
        [
          {
            "node": "Authorized?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Extract API Key",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Subscriptions": {
      "main": [
        [
          {
            "node": "Calculate Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert Reminder Log": {
      "main": [
        [
          {
            "node": "Compose Success Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Reminder Log": {
      "main": [
        [
          {
            "node": "Insert Reminder Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compose Error Response": {
      "main": [
        [
          {
            "node": "Insert Error Log",
            "type": "main",
            "index": 0
          },
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Telegram Reminder": {
      "main": [
        [
          {
            "node": "Telegram Sent?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Telegram Message": {
      "main": [
        [
          {
            "node": "Send Telegram Reminder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compose Success Response": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}