AutomationFlowsEmail & Gmail › Send Overdue Invoice Reminders with iFirma and Slack

Send Overdue Invoice Reminders with iFirma and Slack

Original n8n title: Send Overdue Invoice Payment Reminders with Ifirma, Gmail, Postgrid and Slack

ByŁukasz @lukaszpp on n8n.io

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 reminders to contractors based on configurable time thresholds. The system handles…

Cron / scheduled trigger★★★★★ complexity53 nodesHTTP RequestStop And ErrorSlackGmail
Email & Gmail Trigger: Cron / scheduled Nodes: 53 Complexity: ★★★★★ Added:

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

This workflow follows the Gmail → 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
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "633ea179-d323-4cad-a9e9-8be577dae54d",
      "name": "Encode API Key",
      "type": "n8n-nodes-base.code",
      "position": [
        -80,
        752
      ],
      "parameters": {
        "jsCode": "const apiKeyHex = $('Configuration').first().json[\"API Key Invoice\"];\nconst url = $input.first().json.url ?? \"\";\nconst userLogin =  $('Configuration').first().json[\"Email/Login\"];\nconst keyName = \"faktura\";\nconst requestBody = $input.first().json.requestBody ?? \"\" \nconst message = (url + userLogin + keyName + requestBody).trim();\n\nfunction hexToBytes(hex) {\n    let bytes = \"\";\n    for (let i = 0; i < hex.length; i += 2) {\n        bytes += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n    }\n    return bytes;\n}\n\nfunction hmacSha1(key, message) {\n    var blocksize = 64;\n    if (key.length > blocksize) {\n        key = sha1_bin(key);\n    }\n    \n    var ipad = [], opad = [];\n    for (var i = 0; i < blocksize; i++) {\n        var k = (i < key.length) ? key.charCodeAt(i) : 0;\n        ipad[i] = k ^ 0x36;\n        opad[i] = k ^ 0x5C;\n    }\n\n    var hash = sha1_bin(String.fromCharCode.apply(null, ipad) + message);\n    return sha1_hex(String.fromCharCode.apply(null, opad) + hash);\n\n    function sha1_hex(s) { return bin2hex(sha1_bin(s)); }\n    \n    function bin2hex(bin) {\n        var hex = \"0123456789abcdef\";\n        var str = \"\";\n        for (var i = 0; i < bin.length; i++) {\n            var c = bin.charCodeAt(i);\n            str += hex.charAt((c >> 4) & 0xF) + hex.charAt(c & 0xF);\n        }\n        return str;\n    }\n\n    function sha1_bin(s) {\n        var nblk = ((s.length + 8) >> 6) + 1, blks = new Array(nblk * 16);\n        for (var i = 0; i < nblk * 16; i++) blks[i] = 0;\n        for (var i = 0; i < s.length; i++) blks[i >> 2] |= s.charCodeAt(i) << (24 - (i % 4) * 8);\n        blks[s.length >> 2] |= 0x80 << (24 - (s.length % 4) * 8);\n        blks[nblk * 16 - 1] = s.length * 8;\n        var w = new Array(80), a = 1732584193, b = -271733879, c = -1732584194, d = 271733878, e = -1+1234567890;\n        for (var i = 0; i < blks.length; i += 16) {\n            var olda = a, oldb = b, oldc = c, oldd = d, olde = e;\n            for (var j = 0; j < 80; j++) {\n                if (j < 16) w[j] = blks[i + j];\n                else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);\n                var t = add(add(rol(a, 5), ft(j, b, c, d)), add(add(e, w[j]), kt(j)));\n                e = d; d = c; c = rol(b, 30); b = a; a = t;\n            }\n            a = add(a, olda); b = add(b, oldb); c = add(c, oldc); d = add(d, oldd); e = add(e, olde);\n        }\n        return String.fromCharCode((a >> 24) & 0xFF, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,\n                                   (b >> 24) & 0xFF, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,\n                                   (c >> 24) & 0xFF, (c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF,\n                                   (d >> 24) & 0xFF, (d >> 16) & 0xFF, (d >> 8) & 0xFF, d & 0xFF,\n                                   (e >> 24) & 0xFF, (e >> 16) & 0xFF, (e >> 8) & 0xFF, e & 0xFF);\n    }\n\n    function ft(t, b, c, d) {\n        if (t < 20) return (b & c) | ((~b) & d);\n        if (t < 40) return b ^ c ^ d;\n        if (t < 60) return (b & c) | (b & d) | (c & d);\n        return b ^ c ^ d;\n    }\n    function kt(t) { return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514; }\n    function add(x, y) {\n        var l = (x & 0xFFFF) + (y & 0xFFFF), m = (x >> 16) + (y >> 16) + (l >> 16);\n        return (m << 16) | (l & 0xFFFF);\n    }\n    function rol(n, c) { return (n << c) | (n >>> (32 - c)); }\n}\n\nconst binaryKey = hexToBytes(apiKeyHex);\nconst generatedHash = hmacSha1(binaryKey, message);\nconst authHeader = `IAPIS user=${userLogin}, hmac-sha1=${generatedHash}`;\n// console.log(hmacSha1('111111', '222222')===\"1558ab6c5ab2b0d1cd129b9ad11527cf33486705\")\nreturn { authHeader };"
      },
      "typeVersion": 2
    },
    {
      "id": "d0196ab5-bb90-436d-8112-5fd5bb35c3bb",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -1072,
        752
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e5c314b8-5915-4d18-855d-c201954d0d4a",
              "name": "Email/Login",
              "type": "string",
              "value": ""
            },
            {
              "id": "138ffe50-4a20-4eee-b7ac-1466ca5d6f54",
              "name": "API Key Invoice",
              "type": "string",
              "value": ""
            },
            {
              "id": "e0e86492-3e24-4848-88bb-3e547c889348",
              "name": "X days before due date",
              "type": "number",
              "value": 7
            },
            {
              "id": "efcbc7dc-5fc9-4a2a-a88f-36d7623876f2",
              "name": "Y days after due date",
              "type": "number",
              "value": 7
            },
            {
              "id": "a2862444-9426-4f37-9baa-62cdf08a6d7a",
              "name": "Z days after due date",
              "type": "number",
              "value": 14
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "85c2afd6-cafb-41b0-8ff3-48a2218f7615",
      "name": "URL to Fetch Invoices",
      "type": "n8n-nodes-base.set",
      "position": [
        -240,
        752
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "400dd6ee-1ded-48d8-8dfc-b9e17bc35e8f",
              "name": "url",
              "type": "string",
              "value": "=https://www.ifirma.pl/iapi/faktury.json"
            },
            {
              "id": "6c174c47-5d70-40b7-8ad9-be9f55233118",
              "name": "requestBody",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f96ad6a2-042e-4535-a0dc-140b7104c4dd",
      "name": "Fetch Invoices from System",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Gets all issued (unpaid) invoices from ifirma.pl",
      "position": [
        80,
        752
      ],
      "parameters": {
        "url": "={{ $('URL to Fetch Invoices').first().json.url }}",
        "options": {
          "pagination": {
            "pagination": {
              "parameters": {
                "parameters": [
                  {
                    "name": "strona",
                    "value": "=1"
                  }
                ]
              }
            }
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "status",
              "value": "przeterminowane,nieoplacone,oplaconeCzesciowo"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authentication",
              "value": "={{ $json.authHeader }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "a4284dbd-0447-48ef-8018-77fa8a923d1b",
      "name": "Filter Unpaid Invoices Due Today or Earlier",
      "type": "n8n-nodes-base.filter",
      "position": [
        656,
        752
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "ffa36a46-67a0-4d40-89f5-ea08125e6114",
              "operator": {
                "type": "dateTime",
                "operation": "beforeOrEquals"
              },
              "leftValue": "={{new Date($json.TerminPlatnosci) }}",
              "rightValue": "={{ $now }}"
            },
            {
              "id": "4e0d94d3-878a-4e0e-aa1f-5789a98a479c",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.TerminPlatnosci }}",
              "rightValue": "={{ $now.minus($('Configuration').first().json[\"X days before due date\"], 'days').format('yyyy-MM-dd') }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "77da7546-bd92-4c09-aca4-39569c1d075a",
      "name": "Z Days After Due Date",
      "type": "n8n-nodes-base.filter",
      "position": [
        2352,
        912
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "556037c9-d033-4f89-b23d-f7c15c203d97",
              "operator": {
                "type": "dateTime",
                "operation": "equals"
              },
              "leftValue": "={{ new Date($json.TerminPlatnosci).format('yyyy-LL-dd') }}",
              "rightValue": "={{ $now.minus($('Configuration').first().json[\"Z days after due date\"], 'days').format('yyyy-LL-dd') }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "ccfdb67a-9cd1-43ee-a16a-c8cfca698115",
      "name": "Y Days After Due Date",
      "type": "n8n-nodes-base.filter",
      "position": [
        2352,
        768
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "556037c9-d033-4f89-b23d-f7c15c203d97",
              "operator": {
                "type": "dateTime",
                "operation": "equals"
              },
              "leftValue": "={{ new Date($json.TerminPlatnosci).format('yyyy-LL-dd') }}",
              "rightValue": "={{ $now.minus($('Configuration').first().json[\"Y days after due date\"], 'days').format('yyyy-LL-dd') }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "b9ff5bb2-132c-456e-8d58-617558365696",
      "name": "Assign Endpoint to Invoice Type",
      "type": "n8n-nodes-base.code",
      "position": [
        848,
        752
      ],
      "parameters": {
        "jsCode": "const endpointsMap = {\n    // Standard Domestic & Specialized\n    prz_faktura_kraj: \"fakturakraj\",\n    prz_faktura_wysylka: \"fakturawysylka\",\n    prz_faktura_paragon: \"fakturakraj\",\n    prz_faktura_szczeg_obow: \"fakturakraj\",\n    prz_faktura_met_kasowa: \"fakturakraj\",\n    prz_faktura_odwr_obciaz: \"fakturakraj\",\n    prz_faktura_odwr_obciaz_budowa: \"fakturakraj\",\n    prz_faktura_budowa: \"fakturakraj\",\n    \n    // Foreign Currency / Specialized Domestic\n    prz_faktura_wys_ter_kraj: \"fakturawaluta\",\n\n    // International & EU\n    prz_dostawa_ue_towarow: \"fakturawdt\",\n    prz_eksport_towarow: \"fakturaeksporttowarow\",\n    prz_eksport_dost_uslug_ue: \"fakturaeksportuslugue\",\n    prz_eksport_dost_uslug_nie_ue: \"fakturaeksportuslug\",\n\n    // Proforma (Note: Rodzaj for proforma is often prz_faktura_proforma)\n    prz_faktura_proforma: \"fakturaproformakraj\",\n\n    // Advance & Final (Mapping to the most likely send endpoints)\n    prz_faktura_zal: \"fakturazaliczka\",\n    prz_faktura_kon: \"fakturakoncowa\",\n\n    // Accounts / Non-VAT\n    prz_rachunek_kraj: \"rachunekkraj\",\n    prz_rachunek_zagr: \"rachunekue\"\n};\nfor (const item of $input.all()) {\n  item.json.endpoint = endpointsMap[item.json.Rodzaj];\n}\n\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "c851d834-3d5d-4731-b560-92cc43f4d006",
      "name": "Payment Reminder",
      "type": "n8n-nodes-base.set",
      "position": [
        2688,
        624
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d1103dc3-bb9a-431e-8fa3-c29059c966e6",
              "name": "Reminder Text",
              "type": "string",
              "value": "=This is a reminder about payment for invoice {{ $input.item.json.PelnyNumer }} issued on {{ $input.item.json.DataWystawienia }} for the amount of {{ $input.item.json.Brutto }}"
            },
            {
              "id": "186999e4-c7f5-493c-88cb-20e1fb1e5d6e",
              "name": "Can Pay by Transfer",
              "type": "boolean",
              "value": true
            },
            {
              "id": "e4b7c469-2357-466a-b7d2-4d430ddcba41",
              "name": "Can Pay Cash on Delivery",
              "type": "boolean",
              "value": false
            },
            {
              "id": "0d992cc6-f75a-4f23-b73f-984fff383ea3",
              "name": "Can Pay MTransfer",
              "type": "boolean",
              "value": false
            },
            {
              "id": "c9c45b01-bcb6-465b-95c3-ff196b98d579",
              "name": "Email Address to Send From",
              "type": "string",
              "value": ""
            },
            {
              "id": "3ef65fcd-b002-4424-a7d6-f076e8782570",
              "name": "Email Template",
              "type": "string",
              "value": ""
            },
            {
              "id": "25f97d49-730e-4224-85b5-dbfba2d391ca",
              "name": "Send by Traditional Mail",
              "type": "boolean",
              "value": false
            },
            {
              "id": "dad319d9-1709-4482-b790-8815adca5067",
              "name": "Send E-Invoice",
              "type": "boolean",
              "value": true
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4,
      "alwaysOutputData": false
    },
    {
      "id": "03443c2d-5ce1-4e78-84f5-a6d51c185105",
      "name": "Encode API Key for Sending Reminder",
      "type": "n8n-nodes-base.code",
      "position": [
        3248,
        432
      ],
      "parameters": {
        "jsCode": "const apiKeyHex = $('Configuration').first().json[\"API Key Invoice\"];\nconst url = $input.first().json.url ?? \"\";\nconst userLogin =  $('Configuration').first().json[\"Email/Login\"];\nconst keyName = \"faktura\";\nconst requestBody = $input.first().json.requestBody ?? \"\" \nconst message = url + userLogin + keyName + requestBody;\n\nfunction hexToBytes(hex) {\n    let bytes = \"\";\n    for (let i = 0; i < hex.length; i += 2) {\n        bytes += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n    }\n    return bytes;\n}\n\nfunction hmacSha1(key, message) {\n    var blocksize = 64;\n    if (key.length > blocksize) {\n        key = sha1_bin(key);\n    }\n    \n    var ipad = [], opad = [];\n    for (var i = 0; i < blocksize; i++) {\n        var k = (i < key.length) ? key.charCodeAt(i) : 0;\n        ipad[i] = k ^ 0x36;\n        opad[i] = k ^ 0x5C;\n    }\n\n    var hash = sha1_bin(String.fromCharCode.apply(null, ipad) + message);\n    return sha1_hex(String.fromCharCode.apply(null, opad) + hash);\n\n    function sha1_hex(s) { return bin2hex(sha1_bin(s)); }\n    \n    function bin2hex(bin) {\n        var hex = \"0123456789abcdef\";\n        var str = \"\";\n        for (var i = 0; i < bin.length; i++) {\n            var c = bin.charCodeAt(i);\n            str += hex.charAt((c >> 4) & 0xF) + hex.charAt(c & 0xF);\n        }\n        return str;\n    }\n\n    function sha1_bin(s) {\n        var nblk = ((s.length + 8) >> 6) + 1, blks = new Array(nblk * 16);\n        for (var i = 0; i < nblk * 16; i++) blks[i] = 0;\n        for (var i = 0; i < s.length; i++) blks[i >> 2] |= s.charCodeAt(i) << (24 - (i % 4) * 8);\n        blks[s.length >> 2] |= 0x80 << (24 - (s.length % 4) * 8);\n        blks[nblk * 16 - 1] = s.length * 8;\n        var w = new Array(80), a = 1732584193, b = -271733879, c = -1732584194, d = 271733878, e = -1+1234567890;\n        for (var i = 0; i < blks.length; i += 16) {\n            var olda = a, oldb = b, oldc = c, oldd = d, olde = e;\n            for (var j = 0; j < 80; j++) {\n                if (j < 16) w[j] = blks[i + j];\n                else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);\n                var t = add(add(rol(a, 5), ft(j, b, c, d)), add(add(e, w[j]), kt(j)));\n                e = d; d = c; c = rol(b, 30); b = a; a = t;\n            }\n            a = add(a, olda); b = add(b, oldb); c = add(c, oldc); d = add(d, oldd); e = add(e, olde);\n        }\n        return String.fromCharCode((a >> 24) & 0xFF, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,\n                                   (b >> 24) & 0xFF, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,\n                                   (c >> 24) & 0xFF, (c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF,\n                                   (d >> 24) & 0xFF, (d >> 16) & 0xFF, (d >> 8) & 0xFF, d & 0xFF,\n                                   (e >> 24) & 0xFF, (e >> 16) & 0xFF, (e >> 8) & 0xFF, e & 0xFF);\n    }\n\n    function ft(t, b, c, d) {\n        if (t < 20) return (b & c) | ((~b) & d);\n        if (t < 40) return b ^ c ^ d;\n        if (t < 60) return (b & c) | (b & d) | (c & d);\n        return b ^ c ^ d;\n    }\n    function kt(t) { return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514; }\n    function add(x, y) {\n        var l = (x & 0xFFFF) + (y & 0xFFFF), m = (x >> 16) + (y >> 16) + (l >> 16);\n        return (m << 16) | (l & 0xFFFF);\n    }\n    function rol(n, c) { return (n << c) | (n >>> (32 - c)); }\n}\n\nconst binaryKey = hexToBytes(apiKeyHex);\nconst generatedHash = hmacSha1(binaryKey, message);\nconst authHeader = `IAPIS user=${userLogin}, hmac-sha1=${generatedHash}`;\nconsole.log(hmacSha1('111111', '222222')===\"1558ab6c5ab2b0d1cd129b9ad11527cf33486705\")\nreturn $input.all().map((i)=>{\n  return ({...i.json, authHeader})\n})\nreturn { authHeader };"
      },
      "typeVersion": 2
    },
    {
      "id": "6753a503-ed6f-42dc-bcad-237b2843d268",
      "name": "Send Reminder",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3440,
        432
      ],
      "parameters": {
        "url": "={{ $(\"URL for Sending Reminder\").first().json.url }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendQuery": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "Tekst",
              "value": "={{ $json['Reminder Text'] }}"
            },
            {
              "name": "Przelew",
              "value": "={{ $json['Can Pay by Transfer'] }}"
            },
            {
              "name": "Pobranie",
              "value": "={{ $json['Can Pay Cash on Delivery'] }}"
            },
            {
              "name": "MTransfer",
              "value": "={{ $json['Can Pay MTransfer'] }}"
            },
            {
              "name": "SkrzynkaEmail ",
              "value": "={{ $json['Email Address to Send From'] }}"
            },
            {
              "name": "SzablonEmail ",
              "value": "={{ $json['Email Template'] }}"
            },
            {
              "name": "SkrzynkaEmailOdbiorcy ",
              "value": "={{ $json['EmailDlaFaktury'] }}"
            }
          ]
        },
        "queryParameters": {
          "parameters": [
            {
              "name": "wyslijEfaktura",
              "value": "={{ $json['Send E-Invoice'] }}"
            },
            {
              "name": "wyslijPoczta",
              "value": "={{ $json['Send by Traditional Mail'] }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authentication",
              "value": "={{$json.authHeader}}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "e1d6f5f6-998e-41c4-b13b-9542086b8318",
      "name": "URL for Sending Reminder",
      "type": "n8n-nodes-base.set",
      "position": [
        3056,
        336
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "400dd6ee-1ded-48d8-8dfc-b9e17bc35e8f",
              "name": "url",
              "type": "string",
              "value": "=https://www.ifirma.pl/iapi/{{$input.item.json.endpoint}}/send/{{$input.item.json.FakturaId}}.json"
            },
            {
              "id": "6c174c47-5d70-40b7-8ad9-be9f55233118",
              "name": "requestBody",
              "type": "object",
              "value": "{}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "ea1944d1-9c42-44e1-b63f-828912fdf3ff",
      "name": "Error Fetching Invoices",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        432,
        608
      ],
      "parameters": {
        "errorType": "errorObject",
        "errorObject": "={\n  \"message\": \"Error fetching invoices\",\n  \"body\": {{ JSON.stringify($(\"Fetch Invoices from System\").last().json.response) }}\n}"
      },
      "typeVersion": 1
    },
    {
      "id": "f1a99040-3958-4763-889f-8e93747ed08c",
      "name": "Encode API Key to Fetch Contractor Information",
      "type": "n8n-nodes-base.code",
      "position": [
        1376,
        848
      ],
      "parameters": {
        "jsCode": "const apiKeyHex = $('Configuration').first().json[\"API Key Invoice\"];\nconst url = $input.first().json.url ?? \"\";\nconst userLogin =  $('Configuration').first().json[\"Email/Login\"];\nconst keyName = \"faktura\";\nconst requestBody = $input.first().json.requestBody ?? \"\" \nconst message = url + userLogin + keyName + requestBody;\n\nfunction hexToBytes(hex) {\n    let bytes = \"\";\n    for (let i = 0; i < hex.length; i += 2) {\n        bytes += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n    }\n    return bytes;\n}\n\nfunction hmacSha1(key, message) {\n    var blocksize = 64;\n    if (key.length > blocksize) {\n        key = sha1_bin(key);\n    }\n    \n    var ipad = [], opad = [];\n    for (var i = 0; i < blocksize; i++) {\n        var k = (i < key.length) ? key.charCodeAt(i) : 0;\n        ipad[i] = k ^ 0x36;\n        opad[i] = k ^ 0x5C;\n    }\n\n    var hash = sha1_bin(String.fromCharCode.apply(null, ipad) + message);\n    return sha1_hex(String.fromCharCode.apply(null, opad) + hash);\n\n    function sha1_hex(s) { return bin2hex(sha1_bin(s)); }\n    \n    function bin2hex(bin) {\n        var hex = \"0123456789abcdef\";\n        var str = \"\";\n        for (var i = 0; i < bin.length; i++) {\n            var c = bin.charCodeAt(i);\n            str += hex.charAt((c >> 4) & 0xF) + hex.charAt(c & 0xF);\n        }\n        return str;\n    }\n\n    function sha1_bin(s) {\n        var nblk = ((s.length + 8) >> 6) + 1, blks = new Array(nblk * 16);\n        for (var i = 0; i < nblk * 16; i++) blks[i] = 0;\n        for (var i = 0; i < s.length; i++) blks[i >> 2] |= s.charCodeAt(i) << (24 - (i % 4) * 8);\n        blks[s.length >> 2] |= 0x80 << (24 - (s.length % 4) * 8);\n        blks[nblk * 16 - 1] = s.length * 8;\n        var w = new Array(80), a = 1732584193, b = -271733879, c = -1732584194, d = 271733878, e = -1+1234567890;\n        for (var i = 0; i < blks.length; i += 16) {\n            var olda = a, oldb = b, oldc = c, oldd = d, olde = e;\n            for (var j = 0; j < 80; j++) {\n                if (j < 16) w[j] = blks[i + j];\n                else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);\n                var t = add(add(rol(a, 5), ft(j, b, c, d)), add(add(e, w[j]), kt(j)));\n                e = d; d = c; c = rol(b, 30); b = a; a = t;\n            }\n            a = add(a, olda); b = add(b, oldb); c = add(c, oldc); d = add(d, oldd); e = add(e, olde);\n        }\n        return String.fromCharCode((a >> 24) & 0xFF, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,\n                                   (b >> 24) & 0xFF, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,\n                                   (c >> 24) & 0xFF, (c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF,\n                                   (d >> 24) & 0xFF, (d >> 16) & 0xFF, (d >> 8) & 0xFF, d & 0xFF,\n                                   (e >> 24) & 0xFF, (e >> 16) & 0xFF, (e >> 8) & 0xFF, e & 0xFF);\n    }\n\n    function ft(t, b, c, d) {\n        if (t < 20) return (b & c) | ((~b) & d);\n        if (t < 40) return b ^ c ^ d;\n        if (t < 60) return (b & c) | (b & d) | (c & d);\n        return b ^ c ^ d;\n    }\n    function kt(t) { return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514; }\n    function add(x, y) {\n        var l = (x & 0xFFFF) + (y & 0xFFFF), m = (x >> 16) + (y >> 16) + (l >> 16);\n        return (m << 16) | (l & 0xFFFF);\n    }\n    function rol(n, c) { return (n << c) | (n >>> (32 - c)); }\n}\n\nconst binaryKey = hexToBytes(apiKeyHex);\nconst generatedHash = hmacSha1(binaryKey, message);\nconst authHeader = `IAPIS user=${userLogin}, hmac-sha1=${generatedHash}`;\nconsole.log(hmacSha1('111111', '222222')===\"1558ab6c5ab2b0d1cd129b9ad11527cf33486705\")\nreturn { authHeader };"
      },
      "typeVersion": 2
    },
    {
      "id": "0736637b-ce5b-475c-85fb-5b49bc67de84",
      "name": "URL to Fetch Contractor Information",
      "type": "n8n-nodes-base.set",
      "position": [
        1200,
        848
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "400dd6ee-1ded-48d8-8dfc-b9e17bc35e8f",
              "name": "url",
              "type": "string",
              "value": "=https://www.ifirma.pl/iapi/kontrahenci/id/{{$json.IdentyfikatorKontrahenta}}.json"
            },
            {
              "id": "6c174c47-5d70-40b7-8ad9-be9f55233118",
              "name": "requestBody",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "70190ca0-612d-4f65-b448-a4e559961525",
      "name": "Fetch Contractor Information",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1536,
        848
      ],
      "parameters": {
        "url": "={{ $(\"URL to Fetch Contractor Information\").item.json }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authentication",
              "value": "={{ $json.authHeader }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "cd406348-87ab-4daa-8262-c2dd68cdd8aa",
      "name": "Error Fetching Contractor Data",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        1888,
        656
      ],
      "parameters": {
        "errorType": "errorObject",
        "errorObject": "={\n  \"message\": \"Error fetching contractor data\",\n  \"body\": {{ JSON.stringify($(\"Fetch Invoices from System\").last().json.response) }}\n}"
      },
      "typeVersion": 1
    },
    {
      "id": "e3d10c56-c354-43db-8801-c7faef9a2ac2",
      "name": "Contains Error?",
      "type": "n8n-nodes-base.if",
      "position": [
        256,
        752
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "3413047a-8e5d-4d8b-8370-a5c57ecd4468",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.response.Kod }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "b62dcb1a-b06e-4890-927b-3f3da7f60ff4",
      "name": "Contains Error? 2",
      "type": "n8n-nodes-base.if",
      "position": [
        1712,
        848
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "3413047a-8e5d-4d8b-8370-a5c57ecd4468",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.response.Kod }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "49e7ef49-da66-49f1-af06-34718c2aac40",
      "name": "Extract Contractor Data",
      "type": "n8n-nodes-base.set",
      "position": [
        1888,
        848
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={{ JSON.stringify($input.item.json.response.Wynik[0], null, 2) }}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "b851e357-b230-4603-85b0-bbd1b851c7f8",
      "name": "Deduplicate Contractors",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        1040,
        848
      ],
      "parameters": {
        "compare": "selectedFields",
        "options": {},
        "fieldsToCompare": "IdentyfikatorKontrahenta"
      },
      "typeVersion": 2
    },
    {
      "id": "0293ccfd-73d1-4d7d-bb0e-82a75f03c3db",
      "name": "Invoices for Reminder",
      "type": "n8n-nodes-base.filter",
      "position": [
        2352,
        624
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "556037c9-d033-4f89-b23d-f7c15c203d97",
              "operator": {
                "type": "dateTime",
                "operation": "equals"
              },
              "leftValue": "={{ $json.TerminPlatnosci }}",
              "rightValue": "={{ $now.format('yyyy-LL-dd') }}"
            },
            {
              "id": "92dd91d3-a39e-460a-adf8-61a3ecdc41a4",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.TerminPlatnosci }}",
              "rightValue": "={{ $now.minus($('Configuration').first().json[\"X days before due date\"], 'days').format('yyyy-LL-dd') }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "09a28451-a524-469d-902d-b80e24ff79f6",
      "name": "Extract Invoices",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        432,
        752
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "response.Wynik"
      },
      "typeVersion": 1
    },
    {
      "id": "846b1115-0094-4f7a-aca5-0549ccaafd65",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2112,
        192
      ],
      "parameters": {
        "width": 672,
        "height": 976,
        "content": "## iFirma Overdue Invoice Vindication\n\n**This workflow automates payment recovery for overdue invoices issued in** [iFirma](https://ifirma.pl)**. It fetches unpaid invoices daily, enriches them with full contractor data, and sends the appropriate notification \u2014 payment reminder, pre-trial summons, or court summons \u2014 via Gmail and/or physical mail through** [PostGrid](https://postgrid.com)**. Every action is reported to a Slack channel.**\n\n### How it works\n\nEach day the workflow fetches all unpaid invoices from iFirma using HMAC-SHA1 authenticated API calls, then enriches each invoice with full contractor details. Invoices are split into three escalation tiers based on configurable day thresholds:\n\n- **Reminder** \u2014 due today or X days before due date\n- **Pre-trial summons** \u2014 Y days after due date\n- **Court summons** \u2014 Z days after due date\n\nEach tier can independently send email, a physical letter via PostGrid, or both.\n\n### Setup\n1. Fill out the **Configuration** node with your iFirma login, API key (found under *Data and Configuration \u2192 Extensions and Integrations \u2192 API*), and the X/Y/Z day thresholds\n2. Fill out **Your Company Details** \u2014 used in letter and email templates and as the PostGrid sender address\n3. Configure PostGrid auth in both PostGrid nodes using Custom Auth:\n```json\n{ \"headers\": { \"x-api-key\": \"\" } }\n```\n\n### How to customize\nThe **Payment Reminder**, **Pre-trial summon Reminder**, and **Summons to court Reminder** nodes each have `Send by Traditional Mail` and `Send by Email` toggles \u2014 flip these to control delivery channel per escalation tier. HTML templates can be freely edited in the corresponding **Prepare Mail and Letter HTML Contents** nodes.\n\n\n\n\n\n\n\n\n\n\nNeed help? Contact us at [developers@sailingbyte.com](mailto:developers@sailingbyte.com) or visit [sailingbyte.com](https://sailingbyte.com)!\n\nHappy hacking!"
      },
      "typeVersion": 1
    },
    {
      "id": "66183aef-61c0-4bbe-a606-30df3e1e6759",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1424,
        432
      ],
      "parameters": {
        "color": 3,
        "width": 560,
        "height": 736,
        "content": "## Fill out config\n1. Email/Login - Email or login used to log into the iFirma service\n2. API Key Invoice - API key that can be found at: \nStart > \nData and Configuration > \nExtensions and Integrations > \nAPI\n3. X days before due date - Number of days before payment deadline for reminder - How many days before payment deadline to send reminder (default 7)\n4. Y days after due date - Number of days after payment deadline to send notification about intent to pursue legal action\n5. Z days after due date - Number of days after payment deadline to send notification about pursuing legal action"
      },
      "typeVersion": 1
    },
    {
      "id": "1430302c-976e-4f8f-91f5-2f40eea877f3",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 736,
        "content": "## Step 1: Fetching Invoice List\nFetching the invoice list from the service requires creating an encryption key that is not available to generate using standard N8N tools because it uses SHA-1 algorithm. Because iFirma returns error in response body, we first check if response contains an error, if not we extract items from the body to loop over them."
      },
      "typeVersion": 1
    },
    {
      "id": "d77d322a-b8a5-4b86-818b-de59b80f3477",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 736,
        "content": "## Step 2: Filtering paid invoices, map appropriate endpoints\nInvoices are filtered based on the `TerminPlatnosci` (Payment Deadline) value. Based on `Rodzaj` value from the response we map appropriate endpoint to our object."
      },
      "typeVersion": 1
    },
    {
      "id": "ecccd537-3efd-4322-8ce7-6fc5e7fd4861",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2576,
        432
      ],
      "parameters": {
        "color": 3,
        "width": 320,
        "height": 736,
        "content": "## Configuration\nThese are values that are going to be added to the requests sent to iFirma\n\nValues you can fill in can be found [here](https://api.ifirma.pl/wysylanie-faktur-poczta-tradycyjna-oraz-na-adres-e-mail-kontrahenta/)"
      },
      "typeVersion": 1
    },
    {
      "id": "94810f38-69c1-4718-97ce-ab8874d0f6f2",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 1200,
        "height": 736,
        "content": "## Step 3: Fetch Contractor Data and Merge with Invoice Data\nBecause iFirma sends only partial data about contractor we need to make additional fetches so we have complete object with all necessary data. For these we use `IdentyfikatorKontrahenta`"
      },
      "typeVersion": 1
    },
    {
      "id": "a2383336-11ef-4049-afc9-3471e66bfeae",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2224,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 736,
        "content": "## Step 4: Logic Separation\nAt this point, the logic separates invoices into:\n1. Payment reminder\n2. Notification about intent to pursue legal action\n3. Notification about pursuing legal action"
      },
      "typeVersion": 1
    },
    {
      "id": "14d852be-0d05-4a88-a380-c8efb030eabc",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3024,
        144
      ],
      "parameters": {
        "color": 2,
        "width": 592,
        "height": 464,
        "content": "## Notify Contractor About Approaching Payment Deadline"
      },
      "typeVersion": 1
    },
    {
      "id": "0689d199-a955-4560-8762-7e73b36e301b",
      "name": "Schedule Daily Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "notes": "Runs every day at 9:00 AM",
      "position": [
        -1328,
        752
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a1a49e9a-792e-4b83-ab29-269472412e0f",
      "name": "Your Company Details",
      "type": "n8n-nodes-base.set",
      "position": [
        -624,
        752
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "61884a08-afef-4e5d-9e73-860e5bebe76a",
              "name": "Company Name",
              "type": "string",
              "value": ""
            },
            {
              "id": "03b6e8b6-a48a-4e06-939d-6fd32f0c3234",
              "name": "Email",
              "type": "string",
              "value": ""
            },
            {
              "id": "e5c56d54-f4cb-4856-9cc8-1e99483b1dbe",
              "name": "Phone",
              "type": "string",
              "value": ""
            },
            {
              "id": "e383c8f2-1c46-406e-a079-82228e65859b",
              "name": "Country Code",
              "type": "string",
              "value": ""
            },
            {
              "id": "c7d26305-a13e-43a6-a9c3-025b6f5929fa",
              "name": "City",
              "type": "string",
              "value": ""
            },
            {
              "id": "7c59db00-640e-411f-9ff0-a0aa05e97488",
              "name": "Street",
              "type": "string",
              "value": ""
            },
            {
              "id": "c2bd2535-112b-4766-a4fb-6117fc44d56a",
              "name": "Postal Code",
              "type": "string",
              "value": ""
            },
            {
              "id": "1ef4679e-421a-428a-94d8-93efa37dd951",
              "name": "Swift Code",
              "type": "string",
              "value": ""
            },
            {
              "id": "1c48a217-2fe0-40ff-bdc6-2d31da4ea887",
              "name": "Bank Name",
              "type": "string",
              "value": ""
            },
            {
              "id": "6d70aba1-efe4-4818-be1d-f130538ba642",
              "name": "Bank Account Number",
              "type": "string",
              "value": ""
            },
            {
              "id": "da47e651-5c03-410b-9600-3846411ebafd",
              "name": "TIN (Tax Identification Number)",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "483d1bc0-42f9-4abc-86ff-199591dc1fdf",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -848,
        432
      ],
      "parameters": {
        "color": 3,
        "width": 544,
        "height": 736,
        "content": "## Your company details (Needed for PostGrid)\nThis data is needed for PostGrid API to work. It will be used to send letters in your name for Pre-trial summons and Summons to court when invoice is not paid"
      },
      "typeVersion": 1
    },
    {
      "id": "fc3a4b45-97c8-4fad-93bb-16fa014c08f1",
      "name": "Send pre-trial summon letter via PostGrid",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3440,
        688
      ],
      "parameters": {
        "url": "https://api.postgrid.com/print-mail/v1/letters",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"to\": {\n    \"companyName\": \"{{ $json['KontrahentNazwa'] }}\",\n    \"addressLine1\": \"{{$json['Ulica']}}\",\n    \"city\": \"{{$json['Miejscowosc']}}\",\n    \"provinceOrState\": \"\",\n    \"postalOrZip\": \"{{$json['KodPocztowy']}}\",\n    \"countryCode\": \"{{$json['PrefiksUE']}}\"\n  },\n  \"from\": {\n    \"companyName\": \"{{$('Your Company Details').first().json['Company Name']}}\",\n    \"addressLine1\": \"{{$('Your Company Details').first().json['Street']}}\",\n    \"city\": \"{{$('Your Company Details').first().json['City']}}\",\n    \"postalOrZip\": \"{{$('Your Company Details').first().json['Postal Code']}}\",\n    \"countryCode\": \"{{$('Your Company Details').first().json['Country Code']}}\"\n  },\n  \"html\": {{JSON.stringify($json['Message Content'])}},\n  \"description\": \"Overdue Invoice Reminder - {{$json['FakturaId']}}\",\n  \"color\": false,\n  \"doubleSided\": false,\n  \"addressPlacement\": \"top_first_page\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpCustomAuth"
      },
      "credentials": {
        "httpCustomAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "461a7137-e6a1-46e7-b835-e2eb56e804fc",
      "name": "Payment Reminder Slack Notification",
      "type": "n8n-nodes-base.slack",
      "position": [
        3248,
        240
      ],
      "parameters": {
        "select": "channel",
        "blocksUi": "={\n\t\"blocks\": [\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\"text\": \"Payment reminder has been sent:\"\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"fields\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Invoice No:*\\n{{$json.PelnyNumer}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Overdue:*\\n{{$now.diffTo($json.TerminPlatnosci.toDateTime(), 'days').floor()}} days\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Contractor:*\\n{{$json.KontrahentNazwa}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Payment Deadline:*\\n{{$json.TerminPlatnosci}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Amount:*\\n{{$json.Brutto}} {{$json.Waluta}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Issue Date:*\\n{{$json.DataWystawienia}}\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"type\": \"divider\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\"text\": \"*<fakeLink.toEmployeeProfile.com|View>*\"\n\t\t\t}\n\t\t}\n\t]\n}",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "channel-to-send-invoice-reminders",
          "cachedResultName": "channel-to-send-invoice-reminders"
        },
        "messageType": "block",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "80a48c4c-5f8a-415a-8586-2f172f726ca5",
      "name": "Prepare Mail and Letter HTML Contents for Pre-trial summon",
      "type": "n8n-nodes-base.set",
      "position": [
        3056,
        848
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "393b8e7b-fc27-4d99-bd11-152e16609e25",
              "name": "Message Content",
              "type": "string",
              "value": "=<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <style>\n    body {\n      font-family: Arial, Helvetica, sans-serif;\n      line-height: 1.6;\n      color: #333333;\n      margin: 0;\n      padding: 0;\n      background-color: #ffffff;\n    }\n    .container {\n      max-width: 650px;\n      margin: 0 auto;\n      padding: 40px 30px;\n    }\n    .header {\n      border-bottom: 3px solid #c62828;\n      padding-bottom: 20px;\n      margin-bottom: 30px;\n    }\n    .company-name {\n      font-size: 24px;\n      font-weight: bold;\n      color: #c62828;\n      margin: 0 0 5px 0;\n    }\n    .company-tagline {\n      font-size: 14px;\n      color: #666666;\n      margin: 0;\n    }\n    h1 {\n      color: #c62828;\n      font-size: 22px;\n      margin: 0 0 20px 0;\n      text-transform: uppercase;\n    }\n    .urgent-notice {\n      background-color: #ffebee;\n      border-left: 4px solid #c62828;\n      padding: 15px 20px;\n      margin: 25px 0;\n    }\n    .urgent-notice p {\n      margin: 5px 0;\n      font-size: 15px;\n      font-weight: bold;\n      color: #c62828;\n    }\n    .invoice-details {\n      background-color: #f5f5f5;\n      padding: 20px;\n      border-radius: 5px;\n      margin: 25px 0;\n    }\n    .invoice-details table {\n      width: 100%;\n      border-collapse: collapse;\n    }\n    .invoice-details td {\n      padding: 8px 0;\n      font-size: 15px;\n    }\n    .invoice-details td:first-child {\n      color: #666666;\n      width: 45%;\n    }\n    .invoice-details td:last-child {\n      font-weight: bold;\n      text-align: right;\n    }\n    .amount-due {\n      font-size: 20px;\n      color: #c62828;\n    }\n    .overdue-status {\n      color: #c62828;\n      font-weight: bold;\n    }\n    .legal-warning {\n      background-color: #fff3cd;\n      border: 2px solid #ff6f00;\n      padding: 20px;\n      margin: 25px 0;\n      border-radius: 5px;\n    }\n    .legal-warning h2 {\n      color: #c62828;\n      font-size: 18px;\n      margin: 0 0 15px 0;\n      text-transform: uppercase;\n    }\n    .legal-warning p {\n      margin: 10px 0;\n      font-size: 14px;\n      line-height: 1.8;\n    }\n    .legal-warning ul {\n      margin: 15px 0;\n      padding-left: 20px;\n    }\n    .legal-warning li {\n      margin: 8px 0;\n    }\n    .payment-section {\n      background-color: #f5f5f5;\n      border-left: 4px solid #666666;\n      padding: 20px;\n      margin: 25px 0;\n    }\n    .payment-section h2 {\n      color: #333333;\n      font-size: 18px;\n      margin: 0 0 15px 0;\n    }\n    .payment-details {\n      font-size: 14px;\n      line-height: 1.8;\n    }\n    .payment-details strong {\n      display: inline-block;\n      width: 140px;\n      color: #666666;\n    }\n    .deadline-box {\n      background-color: #c62828;\n      color: #ffffff;\n      padding: 20px;\n      text-align: center;\n      border-radius: 5px;\n      margin: 25px 0;\n    }\n    .deadline-box h2 {\n      margin: 0 0 10px 0;\n      font-size: 20px;\n    }\n    .deadline-box p {\n      margin: 5px 0;\n      font-size: 16px;\n    }\n    .deadline-date {\n      font-size: 24px;\n      font-weight: bold;\n      margin: 10px 0;\n    }\n    .footer {\n      margin-top: 40px;\n      padding-top: 20px;\n      border-top: 1px solid #e0e0e0;\n      font-size: 13px;\n      color: #666666;\n      text-align: center;\n    }\n    .formal-signature {\n      margin-top: 30px;\n      font-size: 14px;\n    }\n    @media print {\n      body { background-color: #ffffff; }\n    }\n  </style>\n</head>\n<body>\n  <div class=\"container\">\n    <!-- Header -->\n    <div class=\"header\">\n      <p class=\"company-name\">{{$('Your Company Details').first().json['Company Name']}}</p>\n      <p class=\"company-tagline\">Software Development & Automation Solutions</p>\n    </div>\n\n    <!-- Main Content -->\n    <h1>Pre-Trial Summons - Notice of Intent to Pursue Legal Action</h1>\n    \n    <p>Dear {{ $json.KontrahentNazwa }},</p>\n    \n    <p><strong>RE: OVERDUE INVOICE - FORMAL NOTICE OF INTENT TO COMMENCE LEGAL PROCEEDINGS</strong></p>\n\n    <!-- Urgent Notice Box -->\n    <div class=\"urgent-notice\">\n      <p>URGENT: Invoice #{{ $json.FakturaId }} is now {{$now.diffTo($json['TerminPlatnosci'].toDateTime(), 'days').abs().floor()}} days overdue</p>\n      <p>Immediate action required to avoid legal proceedings</p>\n    </div>\n\n    <p>This is a formal notice regarding your outstanding payment obligation. Despite our previous communications, payment for the following invoice remains unpaid:</p>\n\n    <!-- Invoice Details -->\n    <div class=\"invoice-details\">\n      <table>\n        <tr>\n          <td>Invoice Number:</td>\n          <td>{{$json['PelnyNumer']}}</td>\n        </tr>\n        <tr>\n          <td>Issue Date:</td>\n          <td>{{$json['DataWystawienia']}}</td>\n        </tr>\n        <tr>\n          <td>Original Due Date:</td>\n          <td>{{$json['TerminPlatnosci']}}</td>\n        </tr>\n        <tr>\n          <td>Days Overdue:</td>\n          <td class=\"overdue-status\">{{$now.diffTo($json['TerminPlatnosci'].toDateTime(), 'days').abs().floor()}} days</td>\n        </tr>\n        <tr style=\"border-top: 2px solid #dddddd;\">\n          <td style=\"padding-top: 15px;\">Outstanding Amount:</td>\n          <td class=\"amount-due\" style=\"padding-top: 15px;\">{{$json.Brutto}} {{ $json.Waluta }}</td>\n        </tr>\n        <tr>\n          <td>Late Payment Interest:</td>\n          <td class=\"overdue-status\">+ Interest accruing daily</td>\n        </tr>\n      </table>\n    </div>\n\n    <!-- Legal Warning -->\n    <div class=\"legal-warning\">\n      <h2>Legal Consequences Notice</h2>\n      <p><strong>We hereby formally notify you of our intent to pursue legal action if this matter is not resolved immediately.</strong></p>\n      \n      <p>Failure to settle this debt within the deadline specified below will result in:</p>\n      <ul>\n        <li><strong>Commencement of legal proceedings</strong> in the appropriate court of law</li>\n        <li><strong>Additional legal costs and court fees</strong> added to the outstanding amount</li>\n        <li><strong>Statutory interest charges</strong> continuing to accrue on the unpaid balance</li>\n        <li><strong>Debt collection agency involvement</strong> and potential credit reporting</li>\n        <li><strong>Enforcement of judgment</strong> through asset seizure or wage garnishment</li>\n        <li><strong>Damage to your business credit rating</strong> and commercial reputation</li>\n      </ul>\n\n      <p>This action may significantly impact your ability to conduct business and obtain credit in the future.</p>\n    </div>\n\n    <!-- Final Deadline -->\n    <div class=\"deadline-box\">\n      <h2>FINAL PAYMENT DEADLINE</h2>\n      <p class=\"deadline-date\">{{$now.plus(( $('Configuration').first().json['Z days after due date'] - $('Configuration').first().json['Y days after due date'] ), 'days').format('MMMM dd, yyyy')}}</p>\n      <p>You have {{( $('Configuration').first().json['Z days after due date'] - $('Configuration').first().json['Y days after due date'] )}} days from the date of this notice to settle this debt in full</p>\n      <p>After this date, legal proceedings will commence without further notice</p>\n    </div>\n\n    <!-- Payment Information -->\n    <div class=\"payment-section\">\n      <h2>Immediate Payment Required</h2>\n      <p><strong>To avoid legal action, payment must be received by the deadline stated above.</strong></p>\n      <div class=\"payment-details\">\n        <p><strong>Bank Name:</strong> {{$('Your Company Details').first().json['Bank Name']}}</p>\n        <p><strong>Account Number:</strong> {{$('Your Company Details').first().json['Bank Account Number']}}</p>\n        <p><strong>SWIFT Code:</strong> {{$('Your Company Details').first().json['Swift Code']}}</p>\n        <p><strong>Payment Reference:</strong> {{$json['PelnyNumer']}} (MANDATORY)</p>\n      </div>\n      <p style=\"margin-top: 15px; color: #c62828;\"><strong>Important:</strong> Include the invoice number in your payment reference to ensure proper allocation.</p>\n    </div>\n\n    <p><strong>Proof of Payment Required:</strong> Upon payment, immediately send proof of transfer to {{$('Your Company Details').first().json['Email']}} to halt legal proceedings.</p>\n\n    <p><strong>Dispute of Debt:</strong> If you dispute this debt, you must provide written notice with supporting documentation within {{ $('Configuration').first().json['Z days after due date'] - $('Configuration').first().json['Y days after due date'] }} business days. Failure to do so will be considered acknowledgment of the debt.</p>\n\n    <!-- Formal Signature -->\n    <div class=\"formal-signature\">\n      <p>This notice is issued in accordance with applicable debt collection regulations.</p>\n      \n      <p style=\"margin-top: 20px;\">\n        Respectfully,<br><br>\n        <strong>Legal & Collections Department</strong><br>\n        {{$('Your Company Details').first().json['Company Name']}}<br>\n        {{$('Your Company Details').first().json['Email']}}<br>\n        {{$('Your Company Details').first().json['Phone']}}\n      </p>\n\n      <p style=\"margin-top: 20px; font-size: 12px; color: #999999;\">\n        <strong>Date of Notice:</strong> {{$now.format('MMMM dd, yyyy')}}<br>\n        <strong>Reference Number:</strong> LEGAL-{{$json['Invoice Id']}}-{{$now.format('yyyyMMdd')}}\n      </p>\n    </div>\n\n    <!-- Footer -->\n    <div class=\"footer\">\n      <p><strong>{{$('Your Company Details').first().json['Company Name']}}</strong></p>\n      <p>{{$('Your Company Details').first().json['Country Code']}} | {{$('Your Company Details').first().json['City']}}, {{$('Your Company Details').first().json['Street']}} | {{$('Your Company Details').first().json['Postal Code']}}</p>\n      <p>TIN: {{$('Your Company Details').first().json['TIN (Tax Identification Number)']}}</p>\n      <hr style=\"margin: 15px 0; border: none; border-top: 1px solid #e0e0e0;\">\n      <p style=\"font-size: 11px; color: #999999;\">\n        <strong>LEGAL NOTICE:</strong> This communication constitutes a formal demand for payment and notice of intent to commence legal proceedings. \n        This message is sent from an automated system but represents official legal correspondence. \n        All communications regarding this matter should be directed to our legal department at the contact details provided above.\n      </p>\n      <p style=\"font-size: 11px; color: #999999; margin-top: 10px;\">\n        Preservation Notice: Please retain this notice for your records. It may be presented as evidence in legal proceedings.\n      </p>\n    </div>\n  </div>\n</body>\n</html>"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "19f79a1e-dc9f-40a0-a215-2fdafde507d2",
      "name": "Pre-trial summon Slack Notification",
      "type": "n8n-nodes-base.slack",
      "position": [
        3056,
        688
      ],
      "parameters": {
        "select": "channel",
        "blocksUi": "={\n\t\"blocks\": [\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\"text\": \"Will to enter a legal route has been sent:\"\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"fields\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Invoice No:*\\n{{$json.PelnyNumer}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Overdue:*\\n{{$now.diffTo($json.TerminPlatnosci.toDateTime(), 'days').floor()}} days\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Contractor:*\\n{{$json.KontrahentNazwa}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Payment Deadline:*\\n{{$json.TerminPlatnosci}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Amount:*\\n{{$json.Brutto}} {{$json.Waluta}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Issue Date:*\\n{{$json.DataWystawienia}}\"\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0A8W2JSHC6",
          "cachedResultName": "ifirma-faktury-przypomnienia"
        },
        "messageType": "block",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "fd9a6089-a857-4616-8dba-be962cb4a1b9",
      "name": "Sticky Note17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3024,
        624
      ],
      "parameters": {
        "color": 2,
        "width": 592,
        "height": 416,
        "content": "## Notify contractor about will to enter legal route"
      },
      "typeVersion": 1
    },
    {
      "id": "2168d77f-5d93-4a93-98e3-4fa3802a414b",
      "name": "Pre-trial summon Reminder",
      "type": "n8n-nodes-base.set",
      "position": [
        2688,
        768
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "25f97d49-730e-4224-85b5-dbfba2d391ca",
              "name": "Send by Traditional Mail",
              "type": "boolean",
              "value": false
            },
            {
              "id": "b11a4b36-b3e7-4f8e-9a44-87294b9e8eb8",
              "name": "Send by Email",
              "type": "boolean",
              "value": true
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4,
      "alwaysOutputData": false
    },
    {
      "id": "6e914bbb-3f83-4e40-a046-c2ee13cdac43",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3024,
        1056
      ],
      "parameters": {
        "color": 2,
        "width": 59

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

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 reminders to contractors based on configurable time thresholds. The system handles…

Source: https://n8n.io/workflows/14310/ — 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

This workflow runs on a schedule to monitor HubSpot deals with upcoming contract expiry dates. It filters deals that are 30, 60, or 90 days away from expiration and processes each one individually. Ba

Gmail, HubSpot, HTTP Request +2
Email & Gmail

This workflow identifies HubSpot deals that have gone untouched for 21+ days and automatically updates their status to Closed Lost. It fetches associated contacts, retrieves their details, and sends p

HubSpot, HTTP Request, Gmail +1
Email & Gmail

This workflow automatically monitors solar energy production every 2 hours by fetching data from the Energidataservice API. If the energy output falls below a predefined threshold, it instantly notifi

HTTP Request, Gmail, Google Sheets +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

What This Flow Does

Gmail, Google Sheets, HTTP Request +1