{
  "name": "04 - Daily Email Summary with GPT-5.6 Luna",
  "active": false,
  "versionId": "47159b60-e72e-441e-a0cd-a891a49a5684",
  "nodes": [
    {
      "parameters": {
        "jsCode": "// ═══════════════════════════════════════════════════════\n// SETTINGS - Configure everything here\n// ═══════════════════════════════════════════════════════\n\n// 🤖 REPLICATE AI MODEL\n// Verified model and reasoning configuration for all workflow stages.\nconst MODEL = 'openai/gpt-5.6-luna';\nconst MODEL_LABEL = 'GPT-5.6 Luna';\nconst REASONING_EFFORT = 'low';\n\n\nconst settings = {\n  // How should the AI sort/prioritize emails?\n  sortingRule: \"Sort by importance. Be STRICT about urgency - only emails requiring same-day action from a real person (not automated alerts) are urgent. Security alerts, bounce notifications, and automated system emails are NOT urgent. If nothing is truly urgent, leave that section empty and say 'Nothing urgent today.' Exclude: sent items, deleted items, drafts, test emails, and bounce-backs from the summary entirely - only include RECEIVED emails that matter.\",\n\n  \n\n  // How far back to fetch emails (hours)\n  lookbackHours: 25,\n\n  // Recipient email (self-send)\n  recipientEmail: \"REPLACE_WITH_YOUR_EMAIL_ADDRESS\",\n\n  // Subject template - {{date}} gets replaced with e.g. \"Tuesday, 24 Feb\"\n  subjectTemplate: \"[{{date}}] - Daily Email Summary\",\n\n  // HTML template showing expected output format\n  summaryTemplate: `<h2>📬 Daily Email Summary</h2>\n\n<h3>🚨 Needs Your Attention</h3>\n<p>Only emails requiring a response or action from you today. If none, say \"Nothing urgent today ✅\"</p>\n<ul>\n  <li><strong>From Name</strong> - Subject<br><span style=\"color:#666\">Why it needs attention</span></li>\n</ul>\n\n<h3>📥 New Mail Worth Knowing About</h3>\n<p>Interesting or useful emails that don't need action right now.</p>\n<ul>\n  <li><strong>From Name</strong> - Subject<br><span style=\"color:#666\">1-line summary</span></li>\n</ul>\n\n<h3>🗑 Noise (skippable)</h3>\n<p>Automated alerts, newsletters, marketing. Listed briefly in case something catches your eye.</p>\n<ul>\n  <li>From Name - Subject</li>\n</ul>\n\n<hr>\n<p style=\"color:#999; font-size:12px\">X emails scanned · Y worth reading · Z skipped</p>`\n};\n\n// Build dynamic date for subject line\nconst now = new Date();\nconst days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\nconst months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];\nconst dateStr = `${days[now.getDay()]}, ${now.getDate()} ${months[now.getMonth()]}`;\nsettings.emailSubject = settings.subjectTemplate.replace('{{date}}', dateStr);\n\n// Compute lookback timestamp (ISO 8601)\nconst lookbackTime = new Date(now.getTime() - settings.lookbackHours * 60 * 60 * 1000);\nsettings.lookbackISO = lookbackTime.toISOString();\n\nsettings.model = MODEL;\nsettings.modelLabel = MODEL_LABEL;\nsettings.reasoningEffort = REASONING_EFFORT;\n\nreturn [{ json: settings }];"
      },
      "id": "settings-node",
      "name": "⚙️ Settings (Summary)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        1536
      ]
    },
    {
      "parameters": {
        "url": "https://graph.microsoft.com/v1.0/me/messages",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftOutlookOAuth2Api",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "$filter",
              "value": "=receivedDateTime ge {{ $json.lookbackISO }}"
            },
            {
              "name": "$select",
              "value": "id,subject,from,receivedDateTime,bodyPreview,parentFolderId,isRead"
            },
            {
              "name": "$top",
              "value": "200"
            },
            {
              "name": "$orderby",
              "value": "receivedDateTime desc"
            }
          ]
        },
        "options": {}
      },
      "id": "fetch-emails",
      "name": "Fetch Emails (Graph API)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1424
      ]
    },
    {
      "parameters": {
        "url": "https://graph.microsoft.com/v1.0/me/mailFolders",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftOutlookOAuth2Api",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "$select",
              "value": "id,displayName"
            },
            {
              "name": "$top",
              "value": "50"
            }
          ]
        },
        "options": {}
      },
      "id": "fetch-folders",
      "name": "Fetch Folder Names",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1632
      ]
    },
    {
      "parameters": {
        "jsCode": "// Merge emails with folder names and settings\nconst settingsData = $('⚙️ Settings (Summary)').first().json;\nconst emailsRaw = $('Fetch Emails (Graph API)').first().json;\nconst foldersRaw = $('Fetch Folder Names').first().json;\n\nconst emails = emailsRaw.value || [];\nconst folders = foldersRaw.value || [];\n\n// Build folder ID -> name map\nconst folderMap = {};\nfor (const f of folders) {\n  folderMap[f.id] = f.displayName;\n}\n\n// Format emails for AI\nconst formattedEmails = emails.map((e, i) => {\n  const folderName = folderMap[e.parentFolderId] || 'Unknown';\n  const fromName = e.from?.emailAddress?.name || e.from?.emailAddress?.address || 'Unknown';\n  const fromEmail = e.from?.emailAddress?.address || '';\n  const preview = (e.bodyPreview || '').substring(0, 300);\n  return `[${folderName}] #${i+1}\\nFrom: ${fromName} <${fromEmail}>\\nSubject: ${e.subject}\\nDate: ${e.receivedDateTime}\\nRead: ${e.isRead}\\nPreview: ${preview}`;\n}).join('\\n\\n---\\n\\n');\n\nreturn [{ json: {\n  ...settingsData,\n  emailCount: emails.length,\n  folderCount: new Set(emails.map(e => e.parentFolderId)).size,\n  formattedEmails\n}}];"
      },
      "id": "merge-data",
      "name": "Merge Emails & Folders",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        896,
        1536
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.replicate.com/v1/models/openai/gpt-5.6-luna/predictions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Prefer",
              "value": "wait=60"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.replicateRequest }}",
        "options": {
          "timeout": 120000
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "id": "ai-summarize",
      "name": "AI Summarize (GPT-5.6 Luna)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        1536
      ]
    },
    {
      "parameters": {
        "jsCode": "function parseStructured(value) {\n  if (value && typeof value === 'object' && !Array.isArray(value)) {\n    if (value.json_output && typeof value.json_output === 'object') return value.json_output;\n    if (value.output?.json_output && typeof value.output.json_output === 'object') return value.output.json_output;\n    if (typeof value.text !== 'string' && typeof value.output?.text !== 'string') return value;\n  }\n  const source = typeof value?.text === 'string'\n    ? value.text\n    : typeof value?.output?.text === 'string'\n      ? value.output.text\n      : value;\n  const text = Array.isArray(source)\n    ? source.map(part => typeof part === 'string' ? part : JSON.stringify(part)).join('')\n    : String(source ?? '');\n  try { return JSON.parse(text); } catch {}\n  const match = text.match(/\\{[\\s\\S]*\\}/);\n  if (!match) return {};\n  try { return JSON.parse(match[0]); } catch { return {}; }\n}\nconst response = $('AI Summarize (GPT-5.6 Luna)').first().json;\nconst settings = $('Merge Emails & Folders').first().json;\nconst parsed = parseStructured(response.output ?? response);\nlet htmlBody = String(parsed.html || '');\nif (!htmlBody || settings.emailCount === 0) {\n  htmlBody = '<h2>📬 Daily Email Summary</h2><p>No new emails in the last ' + settings.lookbackHours + ' hours.</p>';\n}\nreturn [{ json: { subject: settings.emailSubject, recipientEmail: settings.recipientEmail, htmlBody } }];"
      },
      "id": "prepare-email",
      "name": "Prepare Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1344,
        1536
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://graph.microsoft.com/v1.0/me/sendMail",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftOutlookOAuth2Api",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"message\": {\n    \"subject\": \"{{ $json.subject }}\",\n    \"body\": {\n      \"contentType\": \"HTML\",\n      \"content\": {{ JSON.stringify($json.htmlBody) }}\n    },\n    \"toRecipients\": [\n      {\n        \"emailAddress\": {\n          \"address\": \"{{ $json.recipientEmail }}\"\n        }\n      }\n    ]\n  }\n}",
        "options": {}
      },
      "id": "send-email",
      "name": "Send Summary Email",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1568,
        1536
      ]
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineAll",
        "options": {}
      },
      "id": "summary-merge",
      "name": "Merge (Summary)",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.1,
      "position": [
        688,
        1536
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 7
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "Daily 7AM Summary",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        16,
        1536
      ]
    },
    {
      "parameters": {
        "content": "## 4. Daily summary\n\n**Scheduled reporting stage.** At 7:00 AM, fetches the last 25 hours of received mail plus folder names, asks Replicate `openai/gpt-5.6-luna` with **low reasoning** for a concise HTML digest, then emails that digest to the configured recipient.\n\n### Setup after import\n\n1. Select your own Microsoft Outlook OAuth2 credential on every Outlook and Microsoft Graph node.\n2. Create or select a Generic Bearer Auth credential containing your Replicate API token on the GPT-5.6 Luna HTTP Request node.\n3. Review the Settings node and placeholders before activating or running this workflow.",
        "height": 480,
        "width": 1824,
        "color": 7
      },
      "id": "stage-note-daily-summary",
      "name": "Stage 4 — Daily summary",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -112,
        1344
      ]
    },
    {
      "id": "ai-summarize-request-builder",
      "name": "Build AI Summarize (GPT-5.6 Luna) Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        896,
        1536
      ],
      "parameters": {
        "jsCode": "return $input.all().map(item => {\n  const data = item.json;\n  const prompt = \"You are an email summarizer creating a scannable daily digest. \" +\n          \"Exclude sent items, deleted items, drafts, test emails and bounce notifications. \" +\n          \"Only mark an email as needing attention when a human response or action is needed today. \" +\n          \"Automated alerts and marketing are not urgent. Keep each summary to one short line. \" +\n          \"Prefix each item with its folder name in bold brackets. \" +\n          \"Sorting rule: \" + data.sortingRule + \"\\n\\n\" +\n          \"Use this HTML template: \" + data.summaryTemplate + \"\\n\\n\" +\n          \"Total emails: \" + data.emailCount + \" across \" + data.folderCount + \" folders.\\n\\n\" +\n          \"Return only valid JSON in this shape: {\\\"html\\\":\\\"<finished HTML fragment>\\\"}.\\n\\nEMAILS:\\n\" +\n          data.formattedEmails;\n  return {\n    ...item,\n    json: {\n      ...data,\n      replicateRequest: {\n        input: {\n          prompt,\n          reasoning_effort: \"low\",\n          verbosity: 'low',\n          max_completion_tokens: 4000,\n        },\n      },\n    },\n  };\n});"
      }
    }
  ],
  "connections": {
    "⚙️ Settings (Summary)": {
      "main": [
        [
          {
            "node": "Fetch Emails (Graph API)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Folder Names",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Emails (Graph API)": {
      "main": [
        [
          {
            "node": "Merge (Summary)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Folder Names": {
      "main": [
        [
          {
            "node": "Merge (Summary)",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Emails & Folders": {
      "main": [
        [
          {
            "node": "Build AI Summarize (GPT-5.6 Luna) Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Email": {
      "main": [
        [
          {
            "node": "Send Summary Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge (Summary)": {
      "main": [
        [
          {
            "node": "Merge Emails & Folders",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily 7AM Summary": {
      "main": [
        [
          {
            "node": "⚙️ Settings (Summary)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Summarize (GPT-5.6 Luna)": {
      "main": [
        [
          {
            "node": "Prepare Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build AI Summarize (GPT-5.6 Luna) Request": {
      "main": [
        [
          {
            "node": "AI Summarize (GPT-5.6 Luna)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
