{
  "name": "AI Email Summarizer with ntfy Notifications",
  "nodes": [
    {
      "parameters": {
        "postProcessAction": "nothing",
        "options": {}
      },
      "id": "217c9efd-cf35-40c0-a4f4-73a64156a95f",
      "name": "Listen for emails via IMAP",
      "type": "n8n-nodes-base.emailReadImap",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "credentials": {
        "imap": {
          "id": "TuCredencialIMAP",
          "name": "Mail Account IMAP"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "5de54ce4-077e-4468-9ddf-eae6b11eefdd",
              "leftValue": "={{ ($json.subject || '').trim() }}",
              "rightValue": "Contact from My App",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "252f0790-4225-497f-893c-068b549b0bf3",
      "name": "Filter by subject",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        288,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const subject = String($json.subject || '').trim();\n\nconst from =\n  $json.from ||\n  $json.sender ||\n  $json.envelope?.from?.[0]?.address ||\n  'Unknown sender';\n\nconst rawBody =\n  $json.textPlain ||\n  $json.text ||\n  $json.textHtml ||\n  $json.html ||\n  '';\n\nconst cleanBody = String(rawBody)\n  .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n  .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n  .replace(/<[^>]+>/g, ' ')\n  .replace(/\\s+/g, ' ')\n  .trim();\n\nconst bodyForAi = cleanBody || '(no email body)';\n\nconst aiPrompt = `\nAnalyse the following email and extract the key information.\n\nEmail details:\n- Sender: ${from}\n- Subject: ${subject}\n- Body: ${bodyForAi}\n\nReturn ONLY a valid JSON object with this exact structure. This JSON will be processed automatically by another system:\n{\n  \"category\": \"Bug | Support | Billing | Commercial | Inquiry | Other\",\n  \"priority\": \"High | Medium | Low\",\n  \"language\": \"ISO 639-1 code of the detected language, e.g. es, en, fr\",\n  \"summary\": \"Clear summary of the email in a maximum of 180 characters\",\n  \"action\": \"Recommended action for the recipient in a maximum of 80 characters\"\n}\n\nRules:\n- Do not invent information.\n- If the email body is empty, state that in the summary.\n- Write the summary and action in the same language as the email.\n- High priority should be reserved for urgent issues, blocking errors, payment problems, access issues, or anything that prevents the user from using the service.\n`.trim();\n\nreturn [\n  {\n    json: {\n      ...$json,\n      supportEmail: {\n        from,\n        subject,\n        cleanBody: bodyForAi,\n      },\n      aiPrompt,\n    },\n  },\n];"
      },
      "id": "52e5c8de-8024-43ba-a26e-bcd19eea9b2c",
      "name": "Prepare AI input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        560,
        0
      ]
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "gpt-4o-mini",
          "mode": "list",
          "cachedResultName": "GPT-4o mini"
        },
        "responses": {
          "values": [
            {
              "role": "system",
              "content": "You are an assistant that analyses emails and extracts structured information in JSON format. Always return only valid JSON, no markdown, no explanations, and no additional text."
            },
            {
              "role": "user",
              "content": "={{ $json.aiPrompt }}"
            }
          ]
        },
        "builtInTools": {},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 2.3,
      "position": [
        800,
        0
      ],
      "id": "2fe88dc8-0f91-4a74-887d-2077b7f25679",
      "name": "Analyse email with AI",
      "credentials": {
        "openAiApi": {
          "id": "TuCredencialOpenAI",
          "name": "OpenAI account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "function extractText(value) {\n  if (value === null || value === undefined) return '';\n\n  if (typeof value === 'string') return value;\n\n  if (Array.isArray(value)) {\n    return value.map(extractText).filter(Boolean).join('\\n');\n  }\n\n  if (typeof value === 'object') {\n    if (typeof value.output_text === 'string') return value.output_text;\n    if (typeof value.output === 'string') return value.output;\n    if (typeof value.text === 'string') return value.text;\n    if (typeof value.content === 'string') return value.content;\n    if (typeof value.message?.content === 'string') return value.message.content;\n    if (typeof value.choices?.[0]?.message?.content === 'string') return value.choices[0].message.content;\n\n    // OpenAI Responses API style\n    if (Array.isArray(value.output)) {\n      const texts = [];\n      for (const out of value.output) {\n        if (Array.isArray(out.content)) {\n          for (const c of out.content) {\n            if (typeof c.text === 'string') texts.push(c.text);\n          }\n        }\n      }\n      if (texts.length) return texts.join('\\n');\n    }\n  }\n\n  return '';\n}\n\nfunction parseAiJson(raw) {\n  let text = String(raw || '').trim();\n\n  // Remove fences like ```json ... ```\n  text = text\n    .replace(/^```json\\s*/i, '')\n    .replace(/^```\\s*/i, '')\n    .replace(/```$/i, '')\n    .trim();\n\n  // If the model added surrounding text, extract the first JSON object\n  const match = text.match(/\\{[\\s\\S]*\\}/);\n  if (match) text = match[0];\n\n  return JSON.parse(text);\n}\n\nconst rawAiOutput = extractText($json);\n\nlet analysis;\ntry {\n  analysis = parseAiJson(rawAiOutput);\n} catch (error) {\n  analysis = {\n    category: 'Other',\n    priority: 'Medium',\n    summary: rawAiOutput\n      ? String(rawAiOutput).replace(/\\s+/g, ' ').slice(0, 180)\n      : 'Could not generate email summary.',\n    action: 'Review manually',\n  };\n}\n\nconst category = String(analysis.category || 'Other').trim();\nconst priority = String(analysis.priority || 'Medium').trim();\nconst summary = String(analysis.summary || 'No summary available').replace(/\\s+/g, ' ').trim();\nconst action = String(analysis.action || 'Review manually').replace(/\\s+/g, ' ').trim();\n\nconst categoryIcons = {\n  'Bug': '🐞',\n  'Support': '🛠️',\n  'Billing': '💳',\n  'Commercial': '💼',\n  'Inquiry': '❓',\n  'Other': '📩',\n};\n\nconst priorityIcons = {\n  'High': '🔴',\n  'Medium': '🟡',\n  'Low': '🟢',\n};\n\nconst categoryIcon = categoryIcons[category] || '📩';\nconst priorityIcon = priorityIcons[priority] || '🟡';\n\nconst ntfyTitle = `${categoryIcon} ${category} · ${priorityIcon} ${priority}`;\n\nconst ntfyMessage =\n  `📩 New email\\n` +\n  `${summary}\\n\\n` +\n  `Action: ${action}`;\n\nconst ntfyTags = ['email', 'inbox'];\nif (priority === 'High') ntfyTags.push('warning');\nif (category === 'Bug') ntfyTags.push('bug');\n\nreturn [\n  {\n    json: {\n      aiAnalysis: analysis,\n      ntfyTitle,\n      ntfyMessage,\n      ntfyTags: ntfyTags.join(','),\n    },\n  },\n];"
      },
      "id": "f851a39c-4be0-4074-bcd7-fc1cc994d39a",
      "name": "Prepare PUSH notification",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1040,
        0
      ]
    },
    {
      "parameters": {
        "requestMethod": "POST",
        "url": "https://ntfy.sh/your-channel-here",
        "options": {},
        "queryParametersUi": {
          "parameter": [
            {
              "name": "title",
              "value": "={{ $json.ntfyTitle }}"
            },
            {
              "name": "message",
              "value": "={{ $json.ntfyMessage }}"
            },
            {
              "name": "tags",
              "value": "={{ $json.ntfyTags }}"
            }
          ]
        }
      },
      "id": "fe8194db-c4aa-4c9d-85d2-2db8481bb2fd",
      "name": "Send PUSH via ntfy",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 2,
      "position": [
        1280,
        0
      ]
    }
  ],
  "pinData": {},
  "connections": {
    "Listen for emails via IMAP": {
      "main": [
        [
          {
            "node": "Filter by subject",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter by subject": {
      "main": [
        [
          {
            "node": "Prepare AI input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare AI input": {
      "main": [
        [
          {
            "node": "Analyse email with AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyse email with AI": {
      "main": [
        [
          {
            "node": "Prepare PUSH notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare PUSH notification": {
      "main": [
        [
          {
            "node": "Send PUSH via ntfy",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "53042196-d0cf-4686-bd94-f4ef5f0c7d51",
  "meta": {
    "templateCredsSetupCompleted": false,
    "instanceId": "0000000000000000000000000000000000000000000000000000000000000000"
  },
  "nodeGroups": [],
  "id": "ExampleWorkflowEmailAiNtfy",
  "tags": []
}
