{
  "id": "owivLiS5MO6IfmT4",
  "name": "occasion reminder",
  "tags": [],
  "nodes": [
    {
      "id": "0f023a71-85b7-4f0e-a02f-cbfaa9d89100",
      "name": "Every Day at 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1120,
        -64
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "419cacda-a902-427a-8b7c-769534544a21",
      "name": "Date & Time",
      "type": "n8n-nodes-base.dateTime",
      "position": [
        -928,
        -64
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "8f48ee04-443c-4d66-8e80-4d7f478bbef1",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -656,
        -64
      ],
      "parameters": {
        "text": "=Today's date is: {{ $json.currentDate }}\n\nPlease check the Google Sheet resource for any occasions matching today's date (month and day only).\n\nOUTPUT FORMAT:\nWhen occasion is found, return ONLY valid format like this (no other text):\n[{\n  \"hasOccasion\": true,\n  \"email\": \"sarah@email.com\",\n  \"subject\": \"Happy 30th Anniversary Sarah! \ud83c\udf39\u2615\",\n  \"message\": \"Happy 30th Anniversary, my love!...\"\n}]\n\nWhen no occasion is found, return ONLY:\n[{\n  \"hasOccasion\": false\n}]\n\nProceed now.",
        "options": {
          "systemMessage": "=You are my personal assistant specialized in relationship management and special occasion tracking.\n\nYOUR IDENTITY:\n- Thoughtful and detail-oriented\n- You help me maintain strong relationships by ensuring I never miss important dates\n- You have access to a Google Sheet containing all my friends' and family members' special occasions\n\nYOUR CAPABILITIES:\n- You can read and analyze dates from the Google Sheet resource attached\n- You understand different types of occasions: birthdays, anniversaries, graduations, engagements, etc.\n- You can calculate ages and anniversary years accurately\n- You generate personalized, heartfelt messages appropriate for different relationships\n\nYOUR RESPONSIBILITIES:\n1. Check if today matches any occasion in the sheet (compare month and day only, ignore year)\n2. If occasions are found today:\n   - Extract all relevant details (Name, Email, Occasion_Type, Relationship, Personal_Note, Occasion_Date)\n   - Calculate years passed (current year minus occasion year)\n   - Use correct ordinal suffix (1st, 2nd, 3rd, 4th, 21st, 22nd, 23rd, etc.)\n   - Generate warm, personalized messages that reflect the relationship type\n3. If no occasions are found today:\n   - Simply respond: \"No occasions today.\"\n\nMESSAGE GUIDELINES:\n- Adapt tone based on relationship:\n  * Spouse/Partner: Romantic, intimate, loving\n  * Parents: Respectful, warm, appreciative\n  * Siblings: Casual, playful, supportive\n  * Friends: Friendly, fun, genuine\n  * Professional contacts: Formal, respectful, professional\n- Reference the Personal_Note to add authentic, specific touches\n- Keep messages 3-5 sentences\n- Start birthday/anniversary messages with \"Happy [number][suffix] [occasion]!\"\n- Be genuine and heartfelt, never generic or robotic\n- Each message should feel unique and personal\n\nIMPORTANT RULES:\n- Match dates by MM-DD only (ignore the year in comparison)\n- Always calculate the correct age/years for context\n- Never send wishes if the date doesn't match\n- Process each person separately if multiple occasions exist today\n- Maintain privacy and handle personal information carefully\n"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "1ea6f0a1-0017-4b76-9e93-00903bcdd0a0",
      "name": "Get row(s) in sheet in Google Sheets",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        -496,
        128
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/18i8WW1ytW7Zq255Djtj9Xg_uKIsAu9_Oqs452IkRiGQ/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "18i8WW1ytW7Zq255Djtj9Xg_uKIsAu9_Oqs452IkRiGQ",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/18i8WW1ytW7Zq255Djtj9Xg_uKIsAu9_Oqs452IkRiGQ/edit?usp=drivesdk",
          "cachedResultName": "data"
        },
        "descriptionType": "manual",
        "toolDescription": "Get row(s) in sheet in Google Sheets from the sheet named data."
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "94e4ccb3-9794-44f8-872b-e710197ffdec",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -96,
        -64
      ],
      "parameters": {
        "sendTo": "={{ $json.output.email }}",
        "message": "={{ $json.output.message }}",
        "options": {},
        "subject": "={{ $json.output.subject }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "778195ab-b64b-4c06-96e9-db0901d7c63b",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -656,
        112
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "bdeccaf3-6199-4e8a-ab59-3a7d11000589",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        -256,
        -64
      ],
      "parameters": {
        "jsCode": "// Get the string returned by the AI agent\nlet raw = items[0].json.output;\n\n// Sometimes output may already be parsed, handle both cases\nlet parsed;\n\ntry {\n  // Try to parse directly\n  parsed = JSON.parse(raw);\n} catch (e) {\n  // If output contains escaped JSON inside JSON, parse twice\n  try {\n    parsed = JSON.parse(JSON.parse(raw));\n  } catch (e2) {\n    throw new Error(\"AI output is not valid JSON array: \" + raw);\n  }\n}\n\n// Ensure we always get an array\nif (!Array.isArray(parsed)) {\n  parsed = [parsed];\n}\n\n// If no occasions found \u2192 [{ hasOccasion: false }]\nif (parsed.length === 1 && parsed[0].hasOccasion === false) {\n  // Return zero items \u2192 nothing is sent\n  return [];\n}\n\n// Convert each event into its own n8n item\nconst newItems = parsed.map(ev => ({\n  json: {\n    hasOccasion: ev.hasOccasion,\n    email: ev.email,\n    subject: ev.subject,\n    message: ev.message\n  }\n}));\n\nreturn newItems;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "186dc502-4b99-49cf-969f-63412a6938b4",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1776,
        -176
      ],
      "parameters": {
        "width": 528,
        "height": 480,
        "content": "## Overview: Automated Occasion Wisher \n**How it works** \n- Runs daily to check if today matches any birthday, anniversary, or special occasion in your Google Sheet\n- AI Agent reads the sheet and returns list of users having special occasion to wish with details and personalized wishing message\n- If there is no one to wish, no email is sent. If there are multiple people to wish, multiple personalized emails are sent\n\n**Setup steps** \n\n- Connect your Google Sheet containing columns as: Name, Occasion_Date, Email,\tOccasion_Type, Relationship, Personal_Note\n- Insert the AI prompt ensuring strict JSON output (list format only)\n- Configure the Email node for sending the final message\n\n**Customization**\n\n- Edit AI prompt to change message tone, length, or emojis\n- Add support for multiple reminder styles (e.g., early notification)\n- Extend with logging, Slack alerts, or saving sent-email history\n"
      },
      "typeVersion": 1
    },
    {
      "id": "1bd4a7de-1b07-4d63-bbb8-2da1c4881dca",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1152,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 288,
        "content": "## 1. Trigger Everyday and fetch current date "
      },
      "typeVersion": 1
    },
    {
      "id": "cd9810d0-dac7-4280-8dda-8521d3edae99",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -720,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 480,
        "content": "## 2. AI Agent with Google Sheets resource"
      },
      "typeVersion": 1
    },
    {
      "id": "bcd7d0f9-3e97-426e-8344-2c5ad7ac2552",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 288,
        "content": "## 3. Format output from AI Agent and send email if found any event "
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "2ef52b03-2f91-431d-8255-b414812da166",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Date & Time": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every Day at 8 AM": {
      "main": [
        [
          {
            "node": "Date & Time",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet in Google Sheets": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  }
}