{
  "id": "30gz7QjTektkBI2I",
  "meta": {
    "instanceId": "b70db38ce0f0d2b80e12348ac7eb47bc8c0a4592685b356a48495248d533047c",
    "templateCredsSetupCompleted": true
  },
  "name": "365 Service Alerts: Summarize and push alert to Slack",
  "tags": [],
  "nodes": [
    {
      "id": "48e59db6-a492-45bf-8865-0ec258cdd638",
      "name": "Check for 365 Service Alert",
      "type": "n8n-nodes-base.microsoftOutlookTrigger",
      "position": [
        720,
        0
      ],
      "parameters": {
        "output": "raw",
        "filters": {
          "sender": "o365mc@microsoft.com"
        },
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "credentials": {},
      "typeVersion": 1
    },
    {
      "id": "6c392e16-8bae-4336-a7ba-1c2bc39a48ec",
      "name": "Post outage to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        1740,
        0
      ],
      "webhookId": "",
      "parameters": {
        "text": "={{ $json.blocks[0].text.text }}",
        "select": "channel",
        "blocksUi": "={{ $json }}",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": ""
        },
        "messageType": "block",
        "otherOptions": {
          "mrkdwn": true,
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "id": "",
          "name": "Slack - Bot"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "45e2f8bc-48e8-489a-9a5e-bdbbbb399bf7",
      "name": "Clear email alert from mailbox",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        1940,
        0
      ],
      "webhookId": "",
      "parameters": {
        "messageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Check for 365 Service Alert').item.json.id }}"
        },
        "operation": "delete"
      },
      "credentials": {},
      "typeVersion": 2
    },
    {
      "id": "9a9c12d7-e43d-4db8-b764-16112d7ec61e",
      "name": "Summarize service alert",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1180,
        0
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You are a summarization assistant that rewrites Microsoft 365 Service Health alerts into Slack Block Kit-compatible messages for business and IT stakeholders.nnYour output must follow these rules:n- Provide the alert summary as a **single string**, written in Slack mrkdwn format.n- Begin with an appropriate emoji to reflect status:n  - ud83dudfe1 for *Investigating*n  - ud83dudee0ufe0f for *Fix in Progress*n  - u2705 for *Service Restored*n  - u274c for *Outage*n- Include the **service name**, a **short issue summary**, and whether **users may have been affected**.n- Assume the organization has users primarily in the U.S. and Australia. If those regions are affected, state: "_Your users may have been affected._" Otherwise, add: "_No impact expected for your user base._". Dont specify the impact to users if this a Resolved or Service Restored alert.n- The summary must be concise (2u20133 short lines), clear, and neutral in tone.nnAdditionally, return the incident ID (e.g., `EX394347`) and the full incident link separately. These are used to create a button.nnYour final output must be a JSON object with these three fields:n- `summaryText`: the markdown summary text for Slackn- `incidentId`: the incident ID (e.g., EX394347)n- `incidentLink`: the incident URL"
            },
            {
              "content": "=Summarize the following Microsoft 365 alert for Slack.n{{ $json.extractedText }}n{{ $json.incidentLink }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {},
      "typeVersion": 1.8
    },
    {
      "id": "de4751e5-e6c2-44a8-aee5-4d6fb8f09fab",
      "name": "Generate Slack Block",
      "type": "n8n-nodes-base.code",
      "position": [
        1540,
        0
      ],
      "parameters": {
        "jsCode": "return [{n  json: {n    blocks: [n      {n        type: "section",n        text: {n          type: "mrkdwn",n          text: $json.message.content.summaryTextn        },n        accessory: {n          type: "button",n          text: {n            type: "plain_text",n            text: `View ${$input.first().json.message.content.incidentId}`,n            emoji: truen          },n          value: $json.message.content.incidentId,n          url: $json.message.content.incidentLink,n          action_id: "button-action"n        }n      }n    ]n  }n}];n"
      },
      "typeVersion": 2
    },
    {
      "id": "9dd39e09-c467-4eba-ad65-43328cd69aab",
      "name": "Extract M365 Incident text & link",
      "type": "n8n-nodes-base.code",
      "position": [
        940,
        0
      ],
      "parameters": {
        "jsCode": "// This function runs in N8N's Code nodenconst cheerio = require("cheerio");nn// Get the HTML content from the previous nodenconst htmlContent = items[0].json.body.content;nnif (!htmlContent) {n  throw new Error("No HTML content found in the input");n}nn// Load HTML into cheerio (similar to jQuery for Node.js)nconst $ = cheerio.load(htmlContent);nn// Extract the incident link from the anchor tag containing the incident IDnconst incidentAnchor = $('a[href*="servicehealth?message="]').first();nconst incidentLink = incidentAnchor.attr('href') || null;nn// Remove unnecessary elements if neededn$('script, style, head, nav, footer, iframe, img').remove();nn// Extract the text content, preserving basic structurenlet plainText = '';nn// Get the main content (customize selectors based on MS365 alert structure)nconst mainContent = $('body') || $('main') || $('div.main-content');nn// Process text with basic formattingnmainContent.find('h1, h2, h3, h4, h5, p, li, table, div').each((i, el) => {n  const tag = $(el).prop('tagName').toLowerCase();n  const text = $(el).text().trim();n  n  if (!text) return;n  n  // Add appropriate spacing and formattingn  if (tag.startsWith('h')) {n    plainText += `\n\n${text}\n\n`;n  } else if (tag === 'p') {n    plainText += `\n${text}\n`;n  } else if (tag === 'li') {n    plainText += `\n- ${text}`;n  } else {n    plainText += `\n${text}`;n  }n});nn// Clean up extra whitespace and normalize line breaksnplainText = plainText.replace(/\s+\n/g, '\n').replace(/\n{3,}/g, '\n\n').trim();nn// Set the outputnreturn [{n  json: {n    extractedText: plainText,n    incidentLink: incidentLinkn  }n}];"
      },
      "typeVersion": 2
    }
  ],
  "active": true,
  "pinData": {},
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "executionOrder": "v1",
    "saveExecutionProgress": true
  },
  "versionId": "d773a7c3-e50b-45a4-8db0-0186847510f1",
  "connections": {
    "Slack": {
      "main": [
        [
          {
            "node": "Microsoft Outlook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Slack Block": {
      "main": [
        [
          {
            "node": "Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarize service alert": {
      "main": [
        [
          {
            "node": "Generate Slack Block",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for 365 Service Alert": {
      "main": [
        [
          {
            "node": "Extract M365 Incident text & link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract M365 Incident text & link": {
      "main": [
        [
          {
            "node": "Summarize service alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}