{
  "name": "mail-to-landing",
  "nodes": [
    {
      "parameters": {
        "color": 7,
        "width": 1660,
        "height": 160,
        "content": "## 📧 Mail-Postfach → Landing\n\nAlle 2 Minuten Outlook-Postfach via **Nango** (OAuth-Token wird automatisch erneuert) → Mails mit PDF-Anhängen → PDFs werden in `Dokumente/Landing/` abgelegt → UC1-Pipeline (rechnungen-extract) übernimmt von dort."
      },
      "id": "sticky-intro",
      "name": "Beschreibung",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -20,
        0
      ]
    },
    {
      "id": "cron-1",
      "name": "Every 2 min",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        200
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 2
            }
          ]
        }
      }
    },
    {
      "id": "nango-1",
      "name": "Get Outlook Token (Nango)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        280,
        200
      ],
      "parameters": {
        "method": "GET",
        "url": "https://nango.oliverkoehn.com/connection/02c42523-be81-4db0-aa3c-b37e43bcb4b6",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "provider_config_key",
              "value": "outlook"
            },
            {
              "name": "force_refresh",
              "value": "true"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "list-msgs-1",
      "name": "List unread mails with attachments",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        560,
        200
      ],
      "parameters": {
        "method": "GET",
        "url": "https://graph.microsoft.com/v1.0/me/mailFolders/Inbox/messages",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "$filter",
              "value": "hasAttachments eq true and isRead eq false"
            },
            {
              "name": "$select",
              "value": "id,subject,from,hasAttachments"
            },
            {
              "name": "$top",
              "value": "20"
            }
          ]
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Get Outlook Token (Nango)').first().json.credentials.access_token }}"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "split-msgs-1",
      "name": "Split per message",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        840,
        200
      ],
      "parameters": {
        "fieldToSplitOut": "value",
        "options": {}
      }
    },
    {
      "id": "list-att-1",
      "name": "List attachments",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        200
      ],
      "parameters": {
        "method": "GET",
        "url": "=https://graph.microsoft.com/v1.0/me/messages/{{ $json.id }}/attachments",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "$select",
              "value": "id,name,contentType,size"
            }
          ]
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Get Outlook Token (Nango)').first().json.credentials.access_token }}"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "merge-1",
      "name": "Pair msg + atts",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1400,
        200
      ],
      "parameters": {
        "language": "javaScript",
        "jsCode": "// For each msg, emit one item per relevant attachment\nconst ALLOWED = ['application/pdf','application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','application/vnd.openxmlformats-officedocument.presentationml.presentation'];\nconst EXT = {'application/pdf':'.pdf','application/vnd.openxmlformats-officedocument.wordprocessingml.document':'.docx','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':'.xlsx','application/vnd.openxmlformats-officedocument.presentationml.presentation':'.pptx'};\nfunction slug(s){return (s||'mail').toString().toLowerCase().replace(/ä/g,'ae').replace(/ö/g,'oe').replace(/ü/g,'ue').replace(/ß/g,'ss').replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,40)||'mail';}\nconst out = [];\nconst items = $input.all();\nfor (let i=0; i<items.length; i++) {\n  const atts = items[i].json.value || [];\n  // Get the matching message from List unread mails (split-msgs paired by index)\n  const msg = $('Split per message').itemMatching(i).json;\n  const subj = msg.subject || 'no-subject';\n  for (const a of atts) {\n    if (!ALLOWED.includes(a.contentType)) continue;\n    if (a['@odata.type'] && a['@odata.type'].indexOf('fileAttachment') === -1) continue;\n    out.push({ json: { message_id: msg.id, attachment_id: a.id, name: a.name, contentType: a.contentType, ext: EXT[a.contentType], landing_filename: `${slug(subj)}_${a.name}` } });\n  }\n}\nreturn out;"
      }
    },
    {
      "id": "no-att-1",
      "name": "If has attachments",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        0,
        640
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.attachment_id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "fetch-att-1",
      "name": "Fetch attachment binary",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        280,
        640
      ],
      "parameters": {
        "method": "GET",
        "url": "=https://graph.microsoft.com/v1.0/me/messages/{{ $json.message_id }}/attachments/{{ $json.attachment_id }}/$value",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Get Outlook Token (Nango)').first().json.credentials.access_token }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "data"
            }
          }
        }
      }
    },
    {
      "id": "upload-nc-1",
      "name": "Upload to Nextcloud Landing",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        560,
        640
      ],
      "parameters": {
        "method": "PUT",
        "url": "=https://nextcloud.oliverkoehn.com/remote.php/dav/files/admin/Dokumente/Landing/{{ encodeURIComponent($json.landing_filename) }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "contentType": "binaryData",
        "inputDataFieldName": "data",
        "options": {}
      }
    },
    {
      "id": "mark-read-1",
      "name": "Mark mail as read",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        840,
        640
      ],
      "parameters": {
        "method": "PATCH",
        "url": "=https://graph.microsoft.com/v1.0/me/messages/{{ $('Pair msg + atts').item.json.message_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Get Outlook Token (Nango)').first().json.credentials.access_token }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ isRead: true }) }}",
        "options": {}
      },
      "continueOnFail": true
    }
  ],
  "connections": {
    "Every 2 min": {
      "main": [
        [
          {
            "node": "Get Outlook Token (Nango)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Outlook Token (Nango)": {
      "main": [
        [
          {
            "node": "List unread mails with attachments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List unread mails with attachments": {
      "main": [
        [
          {
            "node": "Split per message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split per message": {
      "main": [
        [
          {
            "node": "List attachments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List attachments": {
      "main": [
        [
          {
            "node": "Pair msg + atts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pair msg + atts": {
      "main": [
        [
          {
            "node": "If has attachments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If has attachments": {
      "main": [
        [
          {
            "node": "Fetch attachment binary",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Fetch attachment binary": {
      "main": [
        [
          {
            "node": "Upload to Nextcloud Landing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Nextcloud Landing": {
      "main": [
        [
          {
            "node": "Mark mail as read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": {
    "node:Every 2 min": {
      "recurrenceRules": []
    }
  }
}