AutomationFlowsEmail & Gmail › Create Work4all Incoming Invoices From Microsoft Outlook Supplier Emails

Create Work4all Incoming Invoices From Microsoft Outlook Supplier Emails

ByChristoph Baldow @styx0r on n8n.io

This workflow polls a Microsoft Outlook folder for new supplier emails, sends each message to a companion n8n sub-workflow that creates an incoming invoice in work4all, and then categorizes the email (green/yellow/red) and emails errors via Outlook. Runs every minute on a…

Cron / scheduled trigger★★★★★ complexity41 nodesMicrosoft OutlookExecute Workflow TriggerHTTP RequestN8N Nodes Work4All
Email & Gmail Trigger: Cron / scheduled Nodes: 41 Complexity: ★★★★★ Added:

This workflow corresponds to n8n.io template #16448 — we link there as the canonical source.

This workflow follows the Execute Workflow Trigger → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "YjkCtn2HLgYCvHjO",
  "name": "Automatically create work4all invoices from Outlook inbox emails",
  "tags": [],
  "nodes": [
    {
      "id": "171de5d6-ae50-4edd-8e39-7726adab2e07",
      "name": "Sticky Note - Description",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "width": 460,
        "height": 760,
        "content": "## Automatically create work4all invoices from Outlook inbox emails\n\nPolls an Outlook folder for new supplier emails and turns their invoice attachments into incoming invoices in **work4all**. AI extraction and booking are handled by a built-in extraction sub-workflow on the same canvas; the main flow handles scheduling, fetching, error notifications and Outlook categorisation. Built for teams using **work4all** and **Microsoft Outlook** that want to stop entering supplier invoices by hand.\n\n### How it works\n1. A schedule trigger fetches messages from a chosen Outlook folder that aren't yet categorised red or green.\n2. Each message is passed to the built-in **invoice extraction sub-workflow**, which creates the work4all incoming invoice.\n3. The email is categorised by outcome: **green** = booked, **yellow + green** = booked but please review (supplier / account / tax key was set automatically), **red** = error (a notification email is sent).\n\n### Setup\n1. Add your **Microsoft Outlook** credential (inbox + categorisation nodes).\n2. Add your **work4all** credential (*Extract invoice data* + *Create incoming invoice*).\n3. In *Get inbox messages*, choose the folder to monitor.\n4. In *Set error recipients*, enter the error-notification recipient(s).\n5. Make sure the **Red / Green / Yellow** categories exist in your mailbox.\n\n### Customization\n- Change the polling interval on *Poll inbox every minute*.\n- Adjust which Outlook categories are used.\n\nRequires Microsoft Outlook and work4all accounts, plus the verified `n8n-nodes-work4all` node."
      },
      "typeVersion": 1
    },
    {
      "id": "0cd2f61c-cf67-4223-a922-014117f6037a",
      "name": "Sticky Note - Poll",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 616,
        "height": 376,
        "content": "## 1. Poll the mailbox\nEvery minute, fetch messages from the configured folder that are **not** categorised red or green yet. Choose the folder in *Get inbox messages*."
      },
      "typeVersion": 1
    },
    {
      "id": "b8a2154d-dcf7-429e-b559-eabed86656fe",
      "name": "Sticky Note - Categorise",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1808,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 524,
        "content": "## 3. Categorise the outcome\n**green** = booked \u00b7 **yellow + green** = booked, please review \u00b7 **red** = error. These three categories must exist in your mailbox."
      },
      "typeVersion": 1
    },
    {
      "id": "5b707115-09cd-4218-89ba-1aac2c5585e9",
      "name": "Sticky Note - Extract & book",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1216,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 384,
        "content": "## 2. Extract & book\nEach message is sent to the built-in sub-workflow, which extracts the invoice with AI and books it in work4all. *Has errors?* then routes the result."
      },
      "typeVersion": 1
    },
    {
      "id": "76e7127c-3055-4414-ad21-9246795f6c93",
      "name": "Sticky Note - Error handling",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        912
      ],
      "parameters": {
        "color": 7,
        "width": 760,
        "height": 344,
        "content": "## 4. Error handling\nIf the sub-workflow reports an error, send a notification to the configured recipient(s) and mark the email red."
      },
      "typeVersion": 1
    },
    {
      "id": "83b7fb82-4f4f-4f9b-ab1c-8b6c9b59e4a8",
      "name": "Poll inbox every minute",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        576,
        624
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 1
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "2d17e56f-e8af-4422-a443-d24e781e673e",
      "name": "Set error recipients",
      "type": "n8n-nodes-base.set",
      "position": [
        784,
        624
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-recipients",
              "name": "errorRecipients",
              "type": "string",
              "value": "you@example.com"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "36adae40-8cab-416d-b0ba-fcc4a06f7930",
      "name": "Get inbox messages",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        1008,
        624
      ],
      "parameters": {
        "output": "raw",
        "options": {
          "downloadAttachments": true
        },
        "filtersUI": {
          "values": {
            "filters": {
              "custom": "not(categories/any(c:c eq 'Red category')) and not(categories/any(c:c eq 'Green category'))",
              "foldersToInclude": [
                "REPLACE_WITH_YOUR_FOLDER_ID"
              ]
            }
          }
        },
        "operation": "getAll",
        "returnAll": true
      },
      "retryOnFail": true,
      "typeVersion": 2
    },
    {
      "id": "ae9204b7-4e42-40ec-a943-14cc2f97e383",
      "name": "Run invoice extraction sub-workflow",
      "type": "n8n-nodes-base.executeWorkflow",
      "onError": "continueErrorOutput",
      "position": [
        1264,
        624
      ],
      "parameters": {
        "mode": "each",
        "options": {},
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "YjkCtn2HLgYCvHjO",
          "cachedResultUrl": "/workflow/YjkCtn2HLgYCvHjO",
          "cachedResultName": "Automatically create work4all invoices from Outlook inbox emails"
        },
        "workflowInputs": {
          "value": {},
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "a5442323-6376-481f-a4f5-cadb57d07d5b",
      "name": "Has errors?",
      "type": "n8n-nodes-base.if",
      "position": [
        1584,
        608
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d26a6695-69cd-4253-a407-9ced41587f40",
              "operator": {
                "type": "array",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.errors }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "4708d15c-e761-4e23-8be2-53337c710675",
      "name": "Extract error",
      "type": "n8n-nodes-base.code",
      "position": [
        1600,
        1104
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "return { json: { error: $json.error } };"
      },
      "typeVersion": 2
    },
    {
      "id": "41ddc203-67bc-4ffb-b429-b625a9e1b4ad",
      "name": "Supplier/account/tax key set?",
      "type": "n8n-nodes-base.if",
      "position": [
        1872,
        560
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "d20cbe3b-92aa-4cdf-85ba-1158a2c9f54f",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.invoiceData.ahf_CreateCompleteIncomingInvoice.newSupplierCreated }}",
              "rightValue": true
            },
            {
              "id": "7e81baf3-a1d1-4709-af2a-afbd5f87dd0c",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.invoiceData.ahf_CreateCompleteIncomingInvoice.accountSet }}",
              "rightValue": true
            },
            {
              "id": "9217a33b-c484-4449-84dc-a4223258a114",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.invoiceData.ahf_CreateCompleteIncomingInvoice.taxKeytSet }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "5795c9d9-c98a-4a69-9ab1-d30b7cd3c8a6",
      "name": "Send error email",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        1888,
        1104
      ],
      "parameters": {
        "subject": "=Invoice workflow \"{{ $workflow.name }}\" reported an error",
        "bodyContent": "={{ JSON.stringify($json.error ?? $json.errors ?? {}) }}",
        "toRecipients": "={{ $('Set error recipients').first().json.errorRecipients }}",
        "additionalFields": {}
      },
      "typeVersion": 2
    },
    {
      "id": "5178307a-7c8b-4742-a299-5f18158de3df",
      "name": "Mark message red",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        2144,
        1104
      ],
      "parameters": {
        "messageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Get inbox messages').item.json.id }}"
        },
        "operation": "update",
        "updateFields": {
          "categories": [
            "Red category"
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "816671de-ad95-4d43-9b1a-85aed7fc271c",
      "name": "Mark message yellow + green",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        2112,
        448
      ],
      "parameters": {
        "messageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Get inbox messages').item.json.id }}"
        },
        "operation": "update",
        "updateFields": {
          "categories": [
            "Yellow category",
            "Green category"
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "92a4ab49-4759-483d-84f4-1fa6320f65cb",
      "name": "Mark message green",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        2112,
        656
      ],
      "parameters": {
        "messageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Get inbox messages').item.json.id }}"
        },
        "operation": "update",
        "updateFields": {
          "categories": [
            "Green category"
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "5bec4487-c3af-4641-97ec-49eca4de74d7",
      "name": "Sticky Note - Sub-workflow Description",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3216,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 460,
        "height": 680,
        "content": "## Sub-workflow: extract invoice data from email attachments using AI\nThe main flow calls this part via *Run invoice extraction sub-workflow*. It receives one email with its attachments, reads them with AI, and the **work4all** node books the incoming invoice with the original files attached.\n\n### How it works\n1. Keeps only emails that carry a readable PDF / XML / JSON attachment.\n2. Extracts invoice data in one of two modes (**Extraction config**):\n   - **`backend`** (default) \u2014 work4all AI backend, uses the **work4all** credential.\n   - **`llm`** \u2014 your own **OpenAI-compatible** model (*Call LLM*).\n3. Normalises the result, books the incoming invoice and attaches the files.\n\n### Customization\n- **Extraction mode** \u2014 default is `backend` (work4all AI). For your own model, set **Extraction config** to `llm` and add an **OpenAI-compatible** credential on *Call LLM*.\n- Switch provider/model in *Call LLM* and the `MODEL` constant in *Build LLM messages* (must support **PDF / file input**)."
      },
      "typeVersion": 1
    },
    {
      "id": "053d3dd8-c88c-40bd-8d13-ba86e597306f",
      "name": "Sticky Note - Input",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3760,
        592
      ],
      "parameters": {
        "color": 7,
        "width": 704,
        "height": 448,
        "content": "## 1 \u00b7 Input & validation\nReceives the email from the parent workflow and keeps only items that carry a readable PDF / XML / JSON attachment."
      },
      "typeVersion": 1
    },
    {
      "id": "5744084b-06e4-4e15-bfd2-2c6ae9fe8d7d",
      "name": "Sticky Note - Extract",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5152,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 470,
        "height": 540,
        "content": "## 3 \u00b7 Extract invoice data\nBoth modes read the attachments and return the same structured invoice JSON \u2014 via the work4all AI backend or an OpenAI-compatible LLM (*Build LLM messages* \u2192 *Call LLM*)."
      },
      "typeVersion": 1
    },
    {
      "id": "3d40e4b1-264f-486f-8344-c7e39f278b99",
      "name": "Sticky Note - Book",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6368,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 736,
        "height": 472,
        "content": "## 5 \u00b7 Book in work4all\nNormalises currency and dates, creates the incoming invoice in work4all and attaches the original files."
      },
      "typeVersion": 1
    },
    {
      "id": "800b38a7-21a6-4b15-90a6-2911ab488a22",
      "name": "When called by parent workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        3808,
        864
      ],
      "parameters": {
        "inputSource": "passthrough"
      },
      "typeVersion": 1.1
    },
    {
      "id": "60401e23-4436-4752-9653-0a63b13bc3e7",
      "name": "Has attachments?",
      "type": "n8n-nodes-base.if",
      "position": [
        4032,
        864
      ],
      "parameters": {
        "options": {
          "ignoreCase": false
        },
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "7ea39fb4-a88f-4f20-85d2-6daea27f1c16",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ Object.keys($binary ?? {}).length > 0 }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "31c491c8-d82b-4116-a673-0703889ab124",
      "name": "Build LLM messages",
      "type": "n8n-nodes-base.code",
      "position": [
        5200,
        688
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// \u2699\ufe0f CONFIG: set your OpenAI-compatible model here\nconst MODEL = \"policy/EU-Sonnet-4-6\";\n\nconst item = $input.item;\n\nlet content = [\n  {\n    type: \"text\",\n    text: `\nReturn ONLY valid JSON.\nDo not use markdown.\nDo not wrap in triple backticks.\nDo not add explanations before or after the JSON.\n\nUse exactly this shape:\n{\n  \"found_invoice\": true,\n  \"reason\": \"short explanation\",\n  \"relevantAttachmentKeys\": [\"attachment_0\"],\n  \"invoiceData\": {\n    \"supplierCode\": null,\n    \"supplierName\": null,\n    \"supplierCustomerNumberAtSupplier\": null,\n    \"supplierContactMailAddress\": null,\n    \"supplierIban\": null,\n    \"projectCode\": null,\n    \"projectNumber\": null,\n    \"projectName\": null,\n    \"invoiceNumberSupplier\": null,\n    \"note\": null,\n    \"invoiceDate\": null,\n    \"paymentTermDays\": null,\n    \"discount1Rate\": null,\n    \"discount1Days\": null,\n    \"discount2Rate\": null,\n    \"discount2Days\": null,\n    \"currencyCode\": null,\n    \"invoiceItems\": [\n      {\n        \"account\": null,\n        \"costCenter\": null,\n        \"costGroup\": null,\n        \"projectCode\": null,\n        \"taxRate\": null,\n        \"netAmount\": null,\n        \"grossAmount\": null,\n        \"vatAmount\": null,\n        \"note\": null\n      }\n    ]\n  }\n}\n\nRules:\n- found_invoice = true only if at least one attachment is clearly an incoming supplier invoice / Eingangsrechnung / XRechnung / receipt relevant for accounting.\n- relevantAttachmentKeys must contain the original attachment keys like \"attachment_0\".\n- invoiceData field names must match exactly.\n- The note field should hold a short description, e.g. the supplier company name combined with the invoice period.\n- Each invoiceItems note should be a short note, not more than 50 characters.\n- Dates must be ISO-8601 strings if known, otherwise null.\n- Do not invent values.\n- Aggregate invoiceItems by VAT rate (Mehrwertsteuersatz), not by raw line item.\n- Return one invoiceItems entry per VAT bucket.\n- Sum netAmount, vatAmount and grossAmount per VAT bucket.\n- Only fill account, costCenter, costGroup, projectCode, taxCode if explicitly present in the documents.\n- If no invoice is found, return:\n  {\n    \"found_invoice\": false,\n    \"reason\": \"...\",\n    \"relevantAttachmentKeys\": [],\n    \"invoiceData\": {\n      \"supplierCode\": null,\n      \"supplierName\": null,\n      \"supplierCustomerNumberAtSupplier\": null,\n      \"supplierContactMailAddress\": null,\n      \"supplierIban\": null,\n      \"projectCode\": null,\n      \"projectNumber\": null,\n      \"projectName\": null,\n      \"invoiceNumberSupplier\": null,\n      \"note\": null,\n      \"invoiceDate\": null,\n      \"paymentTermDays\": null,\n      \"discount1Rate\": null,\n      \"discount1Days\": null,\n      \"discount2Rate\": null,\n      \"discount2Days\": null,\n      \"currencyCode\": null,\n      \"invoiceItems\": []\n    }\n  }\n`\n  }\n];\n\nlet has_xml_or_json = false;\n\nif (item.binary) {\n  for (const key of Object.keys(item.binary)) {\n    const file = item.binary[key];\n\n    const mimeType = file.mimeType || \"\";\n    const fileName = file.fileName || \"file\";\n\n    const buffer = await this.helpers.getBinaryDataBuffer(0, key);\n    const base64 = buffer.toString(\"base64\");\n\n    const isPdf =\n      mimeType.includes(\"pdf\") ||\n      fileName.toLowerCase().endsWith(\".pdf\");\n\n    if (isPdf) {\n      has_xml_or_json = true;\n      content.push({\n        type: \"input_file\",\n        filename: fileName,\n        file_data: `data:application/pdf;base64,${base64}`\n      });\n    } else if (\n      mimeType.includes(\"xml\") ||\n      mimeType.includes(\"json\") ||\n      fileName.endsWith(\".xml\") ||\n      fileName.endsWith(\".json\")\n    ) {\n      has_xml_or_json = true;\n      const text = buffer.toString(\"utf-8\");\n\n      content.push({\n        type: \"text\",\n        text: `Content of ${fileName}:\\n${text}`\n      });\n    }\n  }\n}\n\nreturn {\n  json: {\n    has_xml_or_json: has_xml_or_json,\n    model: MODEL,\n    messages: [\n      {\n        role: \"user\",\n        content\n      }\n    ]\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "ba8e471c-8029-4d1c-9053-4c588dfaf234",
      "name": "Has readable attachment?",
      "type": "n8n-nodes-base.if",
      "position": [
        4272,
        768
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a5d662cb-f633-4648-bc88-bcc9362b9630",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ Object.values($binary ?? {}).some(f => /pdf|xml|json/i.test(((f.mimeType || '') + ' ' + (f.fileName || '')))) }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "6c741ed5-bc8d-4881-a04e-45139ff26d97",
      "name": "Call LLM",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5424,
        688
      ],
      "parameters": {
        "url": "https://router.eu.requesty.ai/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "openAiApi"
      },
      "typeVersion": 4.4
    },
    {
      "id": "2dfadd43-7b04-4d1d-b794-7871ee51bbf0",
      "name": "Parse extraction result",
      "type": "n8n-nodes-base.code",
      "position": [
        5824,
        592
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "function removeNullish(value) {\n  if (Array.isArray(value)) {\n    return value.map(removeNullish);\n  }\n  if (value && typeof value === \"object\") {\n    return Object.fromEntries(\n      Object.entries(value)\n        .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n        .map(([k, v]) => [k, removeNullish(v)])\n    );\n  }\n  return value;\n}\n\n// LLM path returns an OpenAI envelope ($json.choices[0].message.content);\n// the work4all AI backend returns the payload directly (often as a plain JSON string).\nlet raw = $json && $json.choices ? $json.choices[0].message.content : $json;\n\nlet parsed;\nif (typeof raw === \"string\") {\n  raw = raw\n    .replace(/^```json\\s*/i, \"\")\n    .replace(/^```\\s*/i, \"\")\n    .replace(/\\s*```$/, \"\")\n    .trim();\n  parsed = raw ? JSON.parse(raw) : {};\n} else {\n  parsed = raw ?? {};\n}\n\nreturn {\n  json: {\n    found_invoice: parsed.found_invoice === true,\n    reason: parsed.reason ?? \"\",\n    relevantAttachmentKeys: parsed.relevantAttachmentKeys ?? [],\n    invoiceData: removeNullish(parsed.invoiceData ?? {}),\n  },\n  binary: $input.item.binary,\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "f57966fa-9f5b-4a44-a99d-7688b7110496",
      "name": "Has invoice?",
      "type": "n8n-nodes-base.if",
      "position": [
        6048,
        592
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "6573bc49-61be-476e-95d7-3f9b4e26b1c9",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.found_invoice }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "38f1e22e-16dd-4fe5-acde-74c2397209b4",
      "name": "Filter attachments & normalize",
      "type": "n8n-nodes-base.code",
      "position": [
        6416,
        576
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const relevantKeys = $json.relevantAttachmentKeys || [];\nconst sourceBinary = $('When called by parent workflow').item.binary || {};\n\nconst keptBinary = Object.fromEntries(\n  relevantKeys\n    .filter((key) => sourceBinary[key])\n    .map((key) => [key, sourceBinary[key]])\n);\n\nconst invoiceData = { ...($json.invoiceData || {}) };\ninvoiceData.currency = \"EUR\";\ndelete invoiceData.currencyCode;\n\nfor (const key of [\"invoiceDate\", \"entryDate\", \"receiptDate\"]) {\n  if (\n    typeof invoiceData[key] === \"string\" &&\n    /^\\d{4}-\\d{2}-\\d{2}$/.test(invoiceData[key])\n  ) {\n    invoiceData[key] = `${invoiceData[key]}T00:00:00Z`;\n  }\n}\n\nconst receiptDate = $('When called by parent workflow').item.json.receivedDateTime;\n\ninvoiceData.receiptDate = receiptDate;\ninvoiceData.entryDate = receiptDate;\n\nreturn {\n  json: {\n    ...$json,\n    invoiceData,\n    relevantAttachmentKeys: relevantKeys,\n  },\n  binary: keptBinary,\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "5f4bdef7-116f-46c6-b387-597cbd5a0ec2",
      "name": "Create incoming invoice",
      "type": "n8n-nodes-work4all.work4all",
      "position": [
        6640,
        576
      ],
      "parameters": {
        "dataMode": "json",
        "attachmentsJson": "={{ ($json.relevantAttachmentKeys || []).map(k => ({ binaryPropertyName: k })) }}",
        "attachmentsMode": "json",
        "invoiceDataJson": "={{$json.invoiceData}}"
      },
      "typeVersion": 1
    },
    {
      "id": "e4ddb9a2-81ce-4c6b-941d-2ae7c1ef88e9",
      "name": "Format work4all result",
      "type": "n8n-nodes-base.code",
      "position": [
        6864,
        576
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "return {\n  json: {\n    messageId: $('When called by parent workflow').item.json.id,\n    errors: $json.errors,\n    invoiceData: $json.data\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "6a1b5693-83dc-4608-953f-ee0f9eb56de0",
      "name": "No invoice - skip",
      "type": "n8n-nodes-base.noOp",
      "position": [
        6656,
        1088
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "31cbc4dc-4cdf-4498-aa0d-c8fa2679d7fc",
      "name": "Build result (no invoice)",
      "type": "n8n-nodes-base.code",
      "position": [
        6896,
        1088
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "return {\n  json: {\n    messageId: $('When called by parent workflow').item.json.id,\n    success: true\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "58a610b5-2455-4926-9122-84bb2a85635f",
      "name": "Extraction config",
      "type": "n8n-nodes-base.set",
      "position": [
        4592,
        592
      ],
      "parameters": {
        "options": {
          "stripBinary": false
        },
        "assignments": {
          "assignments": [
            {
              "id": "a1b2c3d4-+12345678900000001",
              "name": "extractionMode",
              "type": "string",
              "value": "backend"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "e53d64c8-a89e-42ba-a0d8-d9b96681723e",
      "name": "Use work4all AI backend?",
      "type": "n8n-nodes-base.if",
      "position": [
        4816,
        592
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b1b2c3d4-+12345678900000002",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.extractionMode }}",
              "rightValue": "backend"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "fa9fdc89-d625-4613-8035-f74149e5ddf3",
      "name": "Extract invoice data",
      "type": "n8n-nodes-work4all.work4all",
      "position": [
        5424,
        496
      ],
      "parameters": {
        "operation": "extractInvoiceData"
      },
      "typeVersion": 1
    },
    {
      "id": "801e0bce-815a-4005-85a5-7984c248dca6",
      "name": "Sticky Note - Extraction mode",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4560,
        384
      ],
      "parameters": {
        "color": 7,
        "width": 446,
        "height": 416,
        "content": "## 2 \u00b7 Choose extraction mode\nSet `extractionMode` in **Extraction config**: `backend` (work4all AI backend) or `llm` (your own OpenAI-compatible model)."
      },
      "typeVersion": 1
    },
    {
      "id": "38a325fa-4b97-4f69-b053-294e07051c15",
      "name": "Sticky Note - Parse",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5776,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 420,
        "height": 500,
        "content": "## 4 \u00b7 Parse & check\nNormalises either output into the same shape and checks whether an invoice was found. If not, booking is skipped."
      },
      "typeVersion": 1
    },
    {
      "id": "de2b6500-7534-4f57-a7b4-72cac1ff5408",
      "name": "Sticky Note - No invoice",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6608,
        928
      ],
      "parameters": {
        "color": 7,
        "width": 488,
        "height": 320,
        "content": "## 6 \u00b7 No invoice\nItems without a usable invoice end here and return a simple skipped result."
      },
      "typeVersion": 1
    },
    {
      "id": "ab6811e0-9be0-40d0-bcd9-3acf2b4261eb",
      "name": "Sticky Note - Frame Sub workflow",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3072,
        -112
      ],
      "parameters": {
        "color": "#141414",
        "width": 4142,
        "height": 1424,
        "content": "# [Sub workflow] Extract invoice data with AI & book in work4all"
      },
      "typeVersion": 1
    },
    {
      "id": "b0943532-a501-4f0d-acdc-bdec2c26dcc3",
      "name": "Sticky Note - Frame Main workflow",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        -80
      ],
      "parameters": {
        "color": "#141414",
        "width": 2494,
        "height": 1424,
        "content": "# [Main workflow] Poll Outlook inbox, route & categorise emails"
      },
      "typeVersion": 1
    },
    {
      "id": "f1e2d3c4-b5a6-4700-9c00-0002selfref01",
      "name": "Sticky Note - Self reference",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1216,
        208
      ],
      "parameters": {
        "color": 3,
        "width": 360,
        "height": 150,
        "content": "## \u26a0\ufe0f First-time setup\nPoint *Run invoice extraction sub-workflow* to **this workflow** \u2014 it then jumps into the sub-workflow on the right."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "998cdc4d-a42d-4826-bd39-a5a9115dd320",
  "connections": {
    "Call LLM": {
      "main": [
        [
          {
            "node": "Parse extraction result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has errors?": {
      "main": [
        [
          {
            "node": "Send error email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Supplier/account/tax key set?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has invoice?": {
      "main": [
        [
          {
            "node": "Filter attachments & normalize",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No invoice - skip",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract error": {
      "main": [
        [
          {
            "node": "Send error email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has attachments?": {
      "main": [
        [
          {
            "node": "Has readable attachment?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No invoice - skip",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send error email": {
      "main": [
        [
          {
            "node": "Mark message red",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extraction config": {
      "main": [
        [
          {
            "node": "Use work4all AI backend?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No invoice - skip": {
      "main": [
        [
          {
            "node": "Build result (no invoice)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build LLM messages": {
      "main": [
        [
          {
            "node": "Call LLM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get inbox messages": {
      "main": [
        [
          {
            "node": "Run invoice extraction sub-workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract invoice data": {
      "main": [
        [
          {
            "node": "Parse extraction result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set error recipients": {
      "main": [
        [
          {
            "node": "Get inbox messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create incoming invoice": {
      "main": [
        [
          {
            "node": "Format work4all result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse extraction result": {
      "main": [
        [
          {
            "node": "Has invoice?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll inbox every minute": {
      "main": [
        [
          {
            "node": "Set error recipients",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has readable attachment?": {
      "main": [
        [
          {
            "node": "Extraction config",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No invoice - skip",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use work4all AI backend?": {
      "main": [
        [
          {
            "node": "Extract invoice data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build LLM messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supplier/account/tax key set?": {
      "main": [
        [
          {
            "node": "Mark message yellow + green",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark message green",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter attachments & normalize": {
      "main": [
        [
          {
            "node": "Create incoming invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When called by parent workflow": {
      "main": [
        [
          {
            "node": "Has attachments?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run invoice extraction sub-workflow": {
      "main": [
        [
          {
            "node": "Has errors?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Extract error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow polls a Microsoft Outlook folder for new supplier emails, sends each message to a companion n8n sub-workflow that creates an incoming invoice in work4all, and then categorizes the email (green/yellow/red) and emails errors via Outlook. Runs every minute on a…

Source: https://n8n.io/workflows/16448/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Email & Gmail

Keep your IT operations moving, every action email becomes a tracked task, and every security alert lands where it belongs, automatically.

Microsoft Outlook, HTTP Request, Microsoft Teams +1
Email & Gmail

This workflow runs every six hours to pull open Salesforce opportunities, enriches selected deals with Gmail and Microsoft Outlook email context, uses OpenAI to score deal risk, writes the assessment

Slack, Salesforce, Gmail +3
Email & Gmail

This workflow is an automated invoice payment tracking and reminder system for the Polish accounting service iFirma.pl. It monitors unpaid and overdue invoices, then automatically sends escalating rem

HTTP Request, Stop And Error, Slack +1
Email & Gmail

Automatically extract structured information from emails using AI-powered document analysis. This workflow processes emails from specified domains, classifies them by type, and extracts structured dat

Gmail, HTTP Request, AWS S3 +1
Email & Gmail

This workflow automatically extracts recipient email addresses from previously sent emails, validates and cleans the data, and synchronizes only deliverable contacts into the internal Odoo Mailing Lis

HTTP Request