{
  "id": "fCMzWCQbky4BFuco",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "REMINDER-1",
  "tags": [],
  "nodes": [
    {
      "id": "8a52511b-9c38-4b6f-9fd7-8a1bee075754",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -1264,
        -48
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0eaae58a-19ca-4d0f-834b-b71552915e38",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -640,
        -32
      ],
      "parameters": {
        "text": "=Remind input: {{$json[\"message\"][\"text\"]}}, chat_id: {{$json[\"message\"][\"chat\"][\"id\"]}}, user_id: {{$json[\"message\"][\"from\"][\"id\"]}}, now_utc: {{$now}}, default_tz: America/New_York\n",
        "options": {
          "systemMessage": "=Output ONLY valid JSON that matches the schema. No markdown, no extra keys, no wrapping strings.\n"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "cc55f2a2-6169-4798-92bc-5a7d7992a81f",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -656,
        176
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "211b88c8-1538-4ab8-bcb6-ebf01c35fe14",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -480,
        176
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"chat_id\": { \"type\": \"string\" },\n    \"user_id\": { \"type\": \"string\" },\n    \"title\": { \"type\": \"string\" },\n    \"phone\": { \"type\": [\"string\",\"null\"] },\n    \"dateText\": { \"type\": [\"string\",\"null\"] },\n    \"tz\": { \"type\": \"string\" },\n    \"dueISO\": { \"type\": [\"string\",\"null\"] },\n    \"code\": { \"type\": \"string\" }\n  },\n  \"required\": [\"chat_id\",\"user_id\",\"title\",\"tz\",\"code\"]\n}\n"
      },
      "typeVersion": 1.3
    },
    {
      "id": "5b75fe88-d205-489a-a8ca-613dbf13c846",
      "name": "Create a record",
      "type": "n8n-nodes-base.airtable",
      "position": [
        -64,
        -32
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appAkS7G35AGYejcP",
          "cachedResultUrl": "https://airtable.com/appAkS7G35AGYejcP",
          "cachedResultName": "REMINDER-TABLE"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblII8F7Rht6YaOHE",
          "cachedResultUrl": "https://airtable.com/appAkS7G35AGYejcP/tblII8F7Rht6YaOHE",
          "cachedResultName": "Imported table 2"
        },
        "columns": {
          "value": {
            "tz": "={{ $json.output.tz }}",
            "ack": false,
            "code": "={{ $json.code }}",
            "phone": "={{ $json.output.phone }}",
            "title": "={{ $json.output.title }}",
            "due_at": "={{ $json.output.dueISO }}",
            "chat_id": "={{ $json.output.chat_id }}",
            "user_id": "={{ $json.output.user_id }}",
            "next_due_at": "={{ $json.output.dueISO }}"
          },
          "schema": [
            {
              "id": "code",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "code",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "chat_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "chat_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "user_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "user_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "title",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "tz",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "tz",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "due_at",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "due_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "next_due_at",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "next_due_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ack",
              "type": "boolean",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "ack",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "daily",
              "type": "boolean",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "daily",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_sent_for_date",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "last_sent_for_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sent_one_hour_prior_for_date",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "sent_one_hour_prior_for_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "created_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "updated_at",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "updated_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_ping_at",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "last_ping_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "create"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "72142d1f-1e8d-4158-b5c6-a855a184cab9",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        -336,
        -32
      ],
      "parameters": {
        "jsCode": "// 4\u20136 digits; change 1000/9000 if you want 5 or 6 digits\nfunction gen() { return (Math.floor(1000 + Math.random()*9000)).toString(); }\n\n// If you want to reduce collisions per chat, you can later add a quick Airtable\n// search for this chat_id and re-gen if the code exists. For most cases, this is fine.\nreturn $input.all().map(i => ({\n  json: { ...i.json, code: gen() }\n}));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7dbd2c57-e7d9-4773-9a86-7f36c7bc9794",
      "name": "Send a text message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        144,
        -32
      ],
      "parameters": {
        "text": "={{ '\u2705 Reminder set: ' + $json.fields.title + '. Reply ' + $json.fields.code + ' to stop the reminder.' }}\n",
        "chatId": "={{ $json.fields.chat_id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "dda8b68b-b572-4cbe-9e96-16fbf2644316",
      "name": "Search records",
      "type": "n8n-nodes-base.airtable",
      "notes": "NO DATA FOUND",
      "position": [
        -432,
        336
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appAkS7G35AGYejcP",
          "cachedResultUrl": "https://airtable.com/appAkS7G35AGYejcP",
          "cachedResultName": "REMINDER-TABLE"
        },
        "limit": 1,
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblII8F7Rht6YaOHE",
          "cachedResultUrl": "https://airtable.com/appAkS7G35AGYejcP/tblII8F7Rht6YaOHE",
          "cachedResultName": "Imported table 2"
        },
        "options": {},
        "operation": "search",
        "returnAll": false,
        "filterByFormula": "={{ `AND({ack}=0, {chat_id}='${$json.chat_id}', {code}='${$json.code}')` }}\n"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": false,
      "typeVersion": 2.1,
      "alwaysOutputData": true
    },
    {
      "id": "e650464c-ebdd-46e4-979b-2d6cd2e02292",
      "name": "Delete a record",
      "type": "n8n-nodes-base.airtable",
      "position": [
        160,
        432
      ],
      "parameters": {
        "id": "={{ $json.id }}",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appAkS7G35AGYejcP",
          "cachedResultUrl": "https://airtable.com/appAkS7G35AGYejcP",
          "cachedResultName": "REMINDER-TABLE"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblII8F7Rht6YaOHE",
          "cachedResultUrl": "https://airtable.com/appAkS7G35AGYejcP/tblII8F7Rht6YaOHE",
          "cachedResultName": "Imported table 2"
        },
        "operation": "deleteRecord"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "9fb6f754-8b9a-4176-a0d2-63b9b4f75ae2",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        352,
        336
      ],
      "parameters": {
        "mode": "chooseBranch"
      },
      "typeVersion": 3.2
    },
    {
      "id": "40cb1cde-5497-49ef-a4c8-efb5497e31a4",
      "name": "Parse the data",
      "type": "n8n-nodes-base.code",
      "position": [
        -848,
        352
      ],
      "parameters": {
        "jsCode": "const text = ($json.message?.text ?? $json.callback_query?.data ?? '').trim();\nconst chatId = String($json.message?.chat?.id ?? $json.callback_query?.message?.chat?.id ?? '');\n\nconst m = text.match(/\\b(\\d{4,6})\\b/);     // accept 4\u20136 digits anywhere: \"5855\", \"YES 5855\", \"stop 5855\"\nconst yesOnly = /^yes$/i.test(text);       // plain YES (no code)\n\nreturn [{\n  json: {\n    chat_id: chatId,\n    raw: text,\n    code: m ? m[1] : '',\n    has_code: !!m,\n    yes_only: yesOnly\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7c2f096a-8e54-46bf-9b91-50af1700ed5d",
      "name": "SEND CODE NOT FOUND",
      "type": "n8n-nodes-base.telegram",
      "position": [
        176,
        720
      ],
      "parameters": {
        "text": "=I couldn't find a pending reminder for code {{ $('Parse the data').item.json.code }}",
        "chatId": "={{ $('Parse the data').item.json.chat_id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7fa9a7b4-36e9-4929-9bea-3193270bec33",
      "name": "SEND DELETED REMINDER",
      "type": "n8n-nodes-base.telegram",
      "position": [
        624,
        336
      ],
      "parameters": {
        "text": "={{ `\ud83d\uddd1\ufe0f Deleted reminder: ${($json.title ?? $json.fields?.title ?? 'your task')} (code ${$json.code ?? $json.fields?.code ?? ''}).` }}\n",
        "chatId": "={{ $json.chat_id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e04afd7e-d260-4a59-a46b-1429f8794384",
      "name": "CHECK IF CODE IS THERE",
      "type": "n8n-nodes-base.if",
      "position": [
        -688,
        352
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "08c28697-f9ca-43e1-8df4-b867d5849d72",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.has_code }}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "eed485aa-ca9c-437b-ba13-bc34c5b65e65",
      "name": "CODE FOUND?",
      "type": "n8n-nodes-base.if",
      "position": [
        -144,
        336
      ],
      "parameters": {
        "options": {
          "ignoreCase": true
        },
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e901d9ac-4b45-4554-adeb-2488f842569a",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ typeof $json.id !== 'undefined' && $json.id !== null }}",
              "rightValue": ""
            },
            {
              "id": "0de9f46a-45e1-4f64-a52a-821a8236be9b",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "=",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "fb06a7fb-e66c-447a-9fdc-b4f693d64b4c",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        -1040,
        -48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cbf9e362-f611-429a-b5bd-f65e98ccee90",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ /^\\d{4,6}$/.test($json.message.text || '') }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "cef8882f-9137-41ca-9c67-8eefeab3f34b",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2192,
        -448
      ],
      "parameters": {
        "width": 816,
        "height": 1120,
        "content": "\ud83d\udd14 ReminderBot with Telegram + Airtable\n\nThis workflow turns your Telegram into a personal reminder assistant powered by Airtable.\nUsers can set reminders, receive notification messages, and cancel reminders by replying with a unique code.\n\n\u2728 Features\n\n\ud83d\udcf2 Telegram Integration \u2013 Set reminders and manage them directly from Telegram.\n\n\ud83d\uddc4\ufe0f Airtable Storage \u2013 All reminders are stored in Airtable with fields like title, due date, chat_id, and a unique code.\n\n\u23f0 Scheduler \u2013 Automatically checks every few minutes for reminders that are due.\n\n\ud83d\udd11 Unique Cancel Code \u2013 Each reminder is given a code. Reply with that code to stop/delete the reminder.\n\n\u2705 Smart Responses:\n\nIf the reminder is found \u2192 sends deleted confirmation.\n\nIf no reminder matches the code \u2192 sends \u201cCode not found.\u201d\n\n\ud83d\udee0\ufe0f How It Works\n\nTelegram Trigger \u2013 Listens for messages.\n\nIf the message is natural language (\u201cRemind me tomorrow 10am to\u2026\u201d), it is parsed by the AI Agent.\n\nIf the message is a code (e.g., 1234), it is treated as a cancel request.\n\nAI Agent (OpenAI) \u2013 Interprets natural text into a structured reminder:\n\nTitle (task description)\n\nDue date/time\n\nTimezone\n\nGenerates a unique cancellation code.\n\nAirtable \u2013 Stores the reminder with fields: chat_id, title, due_at, code, etc.\n\nScheduler \u2013 Every X minutes:\n\nFetches pending reminders due.\n\nSends them via Telegram (Reminder: do X. Reply 1234 to stop).\n\nCancel Flow \u2013\n\nUser sends back the code.\n\nAirtable is searched by chat_id + code.\n\nIf found \u2192 record is deleted + user notified.\n\nIf not found \u2192 sends back \u201cCode not found.\u201d"
      },
      "typeVersion": 1
    },
    {
      "id": "2a20cac4-d2fc-464e-93da-30093e6fdedb",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        -432
      ],
      "parameters": {
        "color": 5,
        "width": 880,
        "height": 768,
        "content": "Setup Instructions\n\nTelegram Bot\n\nCreate a bot via @BotFather\n.\n\nCopy the API Token into your n8n Telegram Trigger + Send nodes.\n\nAirtable\n\nCreate a base called REMINDER-TABLE.\n\nAdd a table with at least these fields:\n\nchat_id (Text)\n\ntitle (Text)\n\ndue_at (Date/Time)\n\ncode (Number or Text)\n\nAdd your Airtable Personal Access Token in n8n credentials.\n\nOpenAI Agent (optional but recommended)\n\nAdd your OpenAI API key to parse natural text into structured reminders.\n\nScheduler Node\n\nSet an interval (e.g., every 5 minutes) to check for pending reminders.\n\nRun + Test\n\nIn Telegram, send:\n\nRemind me at 6pm to call mom"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "35507b51-91e2-40ee-9a18-e3cf4879b1df",
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Parse the data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "Create a record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "SEND DELETED REMINDER",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CODE FOUND?": {
      "main": [
        [
          {
            "node": "Delete a record",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "SEND CODE NOT FOUND",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse the data": {
      "main": [
        [
          {
            "node": "CHECK IF CODE IS THERE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search records": {
      "main": [
        [
          {
            "node": "CODE FOUND?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a record": {
      "main": [
        [
          {
            "node": "Send a text message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delete a record": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "CHECK IF CODE IS THERE": {
      "main": [
        [
          {
            "node": "Search records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  }
}