diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..1f67719
Binary files /dev/null and b/.DS_Store differ
diff --git a/Invoice_Processing/[PENNYLANE] Auto Invoice from Webhook.json b/Invoice_Processing/[PENNYLANE] Auto Invoice from Webhook.json
new file mode 100644
index 0000000..583160a
--- /dev/null
+++ b/Invoice_Processing/[PENNYLANE] Auto Invoice from Webhook.json
@@ -0,0 +1,746 @@
+{
+ "name": "[PENNYLANE] Auto Invoice from Webhook",
+ "nodes": [
+ {
+ "parameters": {
+ "content": "# [PENNYLANE] Auto Invoice from Webhook\n\n**Trigger:** Webhook POST `/pennylane-invoice`\n**Source:** Any CRM, form, or manual call\n\n---\n\n### Flow\n\n`WH Receive Invoice Data`\n → `Code Validate Payload`\n → `PL Search Customer`\n → `IF Customer Exists`\n → ✅ `Set Customer ID`\n → ❌ `PL Create Customer` → `Set Customer ID`\n → `Code Build Invoice Payload`\n → `PL Create Invoice`\n → `IF Send Email`\n → ✅ `Wait PDF Generation` → `PL Send Invoice Email`\n → `Code Build Notification`\n → `SL Send Notification`\n → `IF Has Notification Email`\n → ✅ `GM Send Notification`\n → `Set Output Response`\n\n---\n\n### Services & Credentials\n\n| Service | Credential |\n|---------|-----------|\n| Pennylane API v2 | Pennylane API (HTTP Header Auth) |\n| Slack (optional) | Slack OAuth |\n| Gmail (optional) | Gmail OAuth |\n\n---\n\n### Node refs for $()\n\n- WH Receive Invoice Data\n- Code Validate Payload\n- PL Search Customer\n- IF Customer Exists\n- Set Customer ID\n- PL Create Customer\n- Code Build Invoice Payload\n- PL Create Invoice\n- IF Send Email\n- Wait PDF Generation\n- PL Send Invoice Email\n- Code Build Notification\n- SL Send Notification\n- IF Has Notification Email\n- GM Send Notification\n- Set Output Response",
+ "height": 752,
+ "width": 480
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ -352,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "2432a919-ea99-4529-969d-2f94a9f44110",
+ "name": "Sticky Note"
+ },
+ {
+ "parameters": {
+ "httpMethod": "POST",
+ "path": "pennylane-invoice",
+ "responseMode": "lastNode",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.webhook",
+ "typeVersion": 2.1,
+ "position": [
+ 272,
+ 32
+ ],
+ "id": "402c4d14-ab30-4baf-85b5-daec4ceb06f4",
+ "name": "WH Receive Invoice Data",
+ "webhookId": "00e37d2c-4cff-46d2-8216-7e2c2006ec47"
+ },
+ {
+ "parameters": {
+ "jsCode": "// Refs: 'WH Receive Invoice Data'\nconst input = $input.first().json;\n\nconst errors = [];\n\nif (!input.customer_name || input.customer_name.trim() === '') {\n errors.push('customer_name is required');\n}\n\nif (!input.customer_email || !input.customer_email.includes('@')) {\n errors.push('customer_email is required and must be valid');\n}\n\nif (!input.items || !Array.isArray(input.items) || input.items.length === 0) {\n errors.push('items array is required and must contain at least one item');\n} else {\n input.items.forEach((item, i) => {\n if (!item.label) errors.push(`items[${i}].label is required`);\n if (!item.quantity || item.quantity <= 0) errors.push(`items[${i}].quantity must be > 0`);\n if (!item.unit_price || item.unit_price <= 0) errors.push(`items[${i}].unit_price must be > 0`);\n });\n}\n\nif (errors.length > 0) {\n throw new Error('Validation failed: ' + errors.join('; '));\n}\n\nreturn [{\n json: {\n customer_name: input.customer_name.trim(),\n customer_email: input.customer_email.trim().toLowerCase(),\n customer_address: input.customer_address || '',\n customer_postal_code: input.customer_postal_code || '00000',\n customer_city: input.customer_city || 'Non renseigné',\n customer_country: input.customer_country || 'FR',\n items: input.items.map(item => ({\n label: item.label,\n quantity: item.quantity,\n unit_price: item.unit_price,\n vat_rate: item.vat_rate || 'FR_200'\n })),\n due_date: input.due_date || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],\n currency: input.currency || 'EUR',\n payment_conditions: input.payment_conditions || '30_days',\n reference: input.reference || null,\n send_email: input.send_email || false,\n notification_email: input.notification_email || null\n }\n}];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 528,
+ 32
+ ],
+ "id": "585218e8-449a-40da-8ce7-d8157998c2f7",
+ "name": "Code Validate Payload"
+ },
+ {
+ "parameters": {
+ "url": "https://app.pennylane.com/api/external/v2/customers",
+ "authentication": "genericCredentialType",
+ "genericAuthType": "httpHeaderAuth",
+ "sendQuery": true,
+ "queryParameters": {
+ "parameters": [
+ {
+ "name": "filter",
+ "value": "=[{\"field\":\"emails\",\"operator\":\"in\",\"value\":\"{{ $json.customer_email }}\"}]"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 832,
+ 32
+ ],
+ "id": "5440a265-819f-42d4-8c26-d83cb51eb219",
+ "name": "PL Search Customer",
+ "credentials": {
+ "httpHeaderAuth": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "conditions": {
+ "options": {
+ "caseSensitive": true,
+ "leftValue": "",
+ "typeValidation": "strict",
+ "version": 3
+ },
+ "conditions": [
+ {
+ "id": "85be8afd-6833-43ba-bb7b-305dd91e9a73",
+ "leftValue": "={{ $json.items.length }}",
+ "rightValue": 0,
+ "operator": {
+ "type": "number",
+ "operation": "gt"
+ }
+ }
+ ],
+ "combinator": "and"
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.if",
+ "typeVersion": 2.3,
+ "position": [
+ 1040,
+ 32
+ ],
+ "id": "8e9e52fa-654e-4b14-b72f-8c57b70744f7",
+ "name": "IF Customer Exists"
+ },
+ {
+ "parameters": {
+ "method": "POST",
+ "url": "https://app.pennylane.com/api/external/v2/company_customers",
+ "authentication": "genericCredentialType",
+ "genericAuthType": "httpHeaderAuth",
+ "sendBody": true,
+ "specifyBody": "json",
+ "jsonBody": "={\n \"name\": \"{{ $('Code Validate Payload').item.json.customer_name }}\",\n \"emails\": [\"{{ $('Code Validate Payload').item.json.customer_email }}\"],\n \"billing_address\": {\n \"address\": \"{{ $('Code Validate Payload').item.json.customer_address }}\",\n \"postal_code\": \"{{ $('Code Validate Payload').item.json.customer_postal_code }}\",\n \"city\": \"{{ $('Code Validate Payload').item.json.customer_city }}\",\n \"country_alpha2\": \"{{ $('Code Validate Payload').item.json.customer_country }}\"\n }\n}",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 1248,
+ 128
+ ],
+ "id": "106b531c-143f-4de7-ba98-8ede691a0d01",
+ "name": "PL Create Customer",
+ "credentials": {
+ "httpHeaderAuth": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "86d02b7a-27bd-4791-a4aa-02b7429dc7bd",
+ "name": "=customer_id",
+ "value": "={{ $json.id ?? $json.items?.[0]?.id }}",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [
+ 1424,
+ 16
+ ],
+ "id": "49c0e376-9f63-4ded-8d0c-917cc7ddf652",
+ "name": "Set Customer ID"
+ },
+ {
+ "parameters": {
+ "method": "POST",
+ "url": "https://app.pennylane.com/api/external/v2/customer_invoices",
+ "authentication": "genericCredentialType",
+ "genericAuthType": "httpHeaderAuth",
+ "sendBody": true,
+ "specifyBody": "json",
+ "jsonBody": "={{ JSON.stringify($json) }}",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 1824,
+ 16
+ ],
+ "id": "c33bd39f-5043-418c-88f9-0aaa8bc25c9d",
+ "name": "PL Create Invoice",
+ "credentials": {
+ "httpHeaderAuth": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "jsCode": "// Refs: 'Set Customer ID', 'Code Validate Payload'\nconst data = $('Code Validate Payload').first().json;\nconst customerId = $('Set Customer ID').first().json.customer_id;\n\nconst invoiceLines = data.items.map(item => ({\n label: item.label,\n quantity: item.quantity,\n unit: 'piece',\n raw_currency_unit_price: String(item.unit_price.toFixed(2)),\n vat_rate: item.vat_rate\n}));\n\nconst today = new Date().toISOString().split('T')[0];\n\nconst payload = {\n customer_id: customerId,\n date: today,\n deadline: data.due_date,\n currency: data.currency,\n invoice_lines: invoiceLines,\n draft: false\n};\n\nif (data.reference) {\n payload.external_reference = data.reference;\n}\n\nreturn [{ json: payload }];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 1648,
+ 16
+ ],
+ "id": "964e6cc9-ff3c-49ab-bda0-ca842e6b380e",
+ "name": "Code Build Invoice Payload"
+ },
+ {
+ "parameters": {
+ "conditions": {
+ "options": {
+ "caseSensitive": true,
+ "leftValue": "",
+ "typeValidation": "strict",
+ "version": 3
+ },
+ "conditions": [
+ {
+ "id": "de5a2cc8-def5-4777-8369-51caea43256f",
+ "leftValue": "={{ $('Code Validate Payload').item.json.send_email }}",
+ "rightValue": "",
+ "operator": {
+ "type": "boolean",
+ "operation": "true",
+ "singleValue": true
+ }
+ }
+ ],
+ "combinator": "and"
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.if",
+ "typeVersion": 2.3,
+ "position": [
+ 2064,
+ 16
+ ],
+ "id": "041e2680-0585-43e8-8df3-4a04b3c32fc3",
+ "name": "IF Send Email"
+ },
+ {
+ "parameters": {
+ "jsCode": "// Refs: 'PL Create Invoice', 'Code Validate Payload'\nconst invoice = $('PL Create Invoice').first().json;\nconst data = $('Code Validate Payload').first().json;\n\nconst message = [\n `New invoice created in Pennylane`,\n ``,\n `Customer: ${data.customer_name}`,\n `Invoice: ${invoice.invoice_number}`,\n `Amount: ${invoice.currency_amount_before_tax} EUR HT`,\n `Total TTC: ${invoice.currency_amount} EUR`,\n `Due date: ${data.due_date}`,\n `Status: ${invoice.status}`,\n data.reference ? `Reference: ${data.reference}` : '',\n ``,\n `View: ${invoice.public_file_url || 'N/A'}`\n].filter(Boolean).join('\\n');\n\nreturn [{\n json: {\n message,\n invoice_id: invoice.id,\n invoice_number: invoice.invoice_number,\n amount: invoice.currency_amount,\n customer_name: data.customer_name,\n public_url: invoice.public_file_url || null\n }\n}];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 2752,
+ 32
+ ],
+ "id": "797f431f-2ffd-42fc-afbc-f76b5806af67",
+ "name": "Code Build Notification"
+ },
+ {
+ "parameters": {
+ "authentication": "oAuth2",
+ "select": "channel",
+ "channelId": {
+ "__rl": true,
+ "value": "",
+ "mode": "list",
+ "cachedResultName": ""
+ },
+ "text": "={{ $json.message }}",
+ "otherOptions": {}
+ },
+ "type": "n8n-nodes-base.slack",
+ "typeVersion": 2.4,
+ "position": [
+ 3296,
+ -112
+ ],
+ "id": "d5f88019-04c7-43e7-a813-efbb52b954a5",
+ "name": "SL Send Notification",
+ "webhookId": "f2c28848-283c-4ca1-b9c7-e47d6da805c3",
+ "credentials": {
+ "slackOAuth2Api": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "c5fe5a2e-f759-4afd-907f-eacb7251481b",
+ "name": "success",
+ "value": true,
+ "type": "boolean"
+ },
+ {
+ "id": "03e80656-7f61-46cd-98d4-0e7daf6636d3",
+ "name": "invoice_id",
+ "value": "={{ $('Code Build Notification').item.json.invoice_id }}",
+ "type": "string"
+ },
+ {
+ "id": "0f54ae5d-87d0-473f-9076-3b17bc24ec0d",
+ "name": "invoice_number",
+ "value": "={{ $('Code Build Notification').item.json.invoice_number }}",
+ "type": "string"
+ },
+ {
+ "id": "878b0587-95ee-4b69-8853-f51c1c356dfa",
+ "name": "amount",
+ "value": "={{ $('Code Build Notification').item.json.amount }}",
+ "type": "string"
+ },
+ {
+ "id": "ef1c38e7-d845-4604-bb8f-908fcbbe4146",
+ "name": "customer",
+ "value": "={{ $('Code Build Notification').item.json.customer_name }}",
+ "type": "string"
+ },
+ {
+ "id": "93d69fa3-86ca-4f0b-8db3-94d7177c10cf",
+ "name": "public_url",
+ "value": "={{ $('Code Build Notification').item.json.public_url }}",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [
+ 3648,
+ 32
+ ],
+ "id": "8d44a126-0a95-4283-ab89-2ebf6e40baa7",
+ "name": "Set Output Response"
+ },
+ {
+ "parameters": {
+ "amount": 30
+ },
+ "type": "n8n-nodes-base.wait",
+ "typeVersion": 1.1,
+ "position": [
+ 2288,
+ -48
+ ],
+ "id": "f12591be-0ef5-4cc5-8232-a9058b77d8b6",
+ "name": "Wait PDF Generation",
+ "webhookId": "a4b7c8a5-7345-417e-8c05-4519039a97b7"
+ },
+ {
+ "parameters": {
+ "method": "POST",
+ "url": "=https://app.pennylane.com/api/external/v2/customer_invoices/{{ $('PL Create Invoice').item.json.id }}/send_by_email",
+ "authentication": "genericCredentialType",
+ "genericAuthType": "httpHeaderAuth",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 2512,
+ -48
+ ],
+ "id": "892471ed-1b8a-427c-a310-0f40ec49b632",
+ "name": "PL Send Invoice Email",
+ "credentials": {
+ "httpHeaderAuth": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "sendTo": "={{ $('Code Validate Payload').item.json.notification_email }}",
+ "subject": "=[Pennylane] Invoice {{ $('Code Build Notification').item.json.invoice_number }} created for {{ $('Code Build Notification').item.json.customer_name }}",
+ "message": "=
\n
\n
New Invoice Created
\n \n
\n
\n \n | Customer | \n {{ $('Code Build Notification').item.json.customer_name }} | \n
\n \n | Invoice | \n {{ $('Code Build Notification').item.json.invoice_number }} | \n
\n \n | Amount HT | \n {{ $('PL Create Invoice').item.json.currency_amount_before_tax }} EUR | \n
\n \n | Total TTC | \n {{ $('Code Build Notification').item.json.amount }} EUR | \n
\n \n | Due date | \n {{ $('Code Validate Payload').item.json.due_date }} | \n
\n \n | Status | \n {{ $('PL Create Invoice').item.json.status }} | \n
\n
\n
\n
\n
Sent automatically by n8n-pennylane-auto-invoicing
\n
",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.gmail",
+ "typeVersion": 2.2,
+ "position": [
+ 3216,
+ 176
+ ],
+ "id": "b73e78af-3604-4d70-a583-f0074877a411",
+ "name": "GM Send Notification",
+ "webhookId": "33b592da-5502-4eb7-ad16-3735811be9dc",
+ "credentials": {
+ "gmailOAuth2": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "conditions": {
+ "options": {
+ "caseSensitive": true,
+ "leftValue": "",
+ "typeValidation": "strict",
+ "version": 3
+ },
+ "conditions": [
+ {
+ "id": "94e188dc-d883-4a77-aac1-06b5bed2dbff",
+ "leftValue": "={{ $('Code Validate Payload').item.json.notification_email }}",
+ "rightValue": "",
+ "operator": {
+ "type": "string",
+ "operation": "notEmpty",
+ "singleValue": true
+ }
+ }
+ ],
+ "combinator": "and"
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.if",
+ "typeVersion": 2.3,
+ "position": [
+ 2992,
+ 96
+ ],
+ "id": "88c6cf59-4ad7-4872-a7b1-116590237115",
+ "name": "IF Has Notification Email"
+ },
+ {
+ "parameters": {
+ "content": "## 1. Input\n\nReceives invoice data via POST webhook.\nAny CRM, form, or script can call this endpoint.\n\nSee `examples/` folder in the GitHub repo\nfor sample payloads.",
+ "height": 624,
+ "color": 5
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 192,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "adf964cd-ccdd-44c7-aaa3-4b035df2fa44",
+ "name": "Sticky Note1"
+ },
+ {
+ "parameters": {
+ "content": "## 2. Validation\n\nChecks required fields:\n- customer_name\n- customer_email\n- items[] with label, quantity, unit_price\n\nApplies defaults:\n- country: FR\n- currency: EUR\n- vat_rate: FR_200\n- due_date: +30 days\n- payment_conditions: 30_days",
+ "height": 624,
+ "width": 304,
+ "color": 5
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 432,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "50f9b391-c5ba-4b8e-8738-687added6648",
+ "name": "Sticky Note2"
+ },
+ {
+ "parameters": {
+ "content": "## 3. Customer lookup\n\nSearches Pennylane for an existing customer\nby email. Creates one if not found.\n\nEndpoint: GET /customers (filter by emails)\nEndpoint: POST /company_customers\n\nRequired billing_address fields:\naddress, postal_code, city, country_alpha2",
+ "height": 624,
+ "width": 832,
+ "color": 3
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 736,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "6e1dd244-3c60-405c-ae59-f1dc8a60a63c",
+ "name": "Sticky Note3"
+ },
+ {
+ "parameters": {
+ "content": "## 4. Invoice creation\n\nBuilds the invoice payload with line items,\nVAT, and deadline, then sends it to Pennylane.\n\nEndpoint: POST /customer_invoices\n\nAmounts must be strings (e.g. \"1500.00\").\nVAT codes: FR_200 (20%), FR_100 (10%),\nFR_055 (5.5%), exempt (0%).\n\nSet draft: true to create without finalizing.",
+ "height": 624,
+ "width": 432,
+ "color": 3
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 1568,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "4a4c4708-448f-4b1c-9a21-ba0bcdf1f6ca",
+ "name": "Sticky Note4"
+ },
+ {
+ "parameters": {
+ "content": "## 5. Email sending (optional)\n\nIf send_email is true in the payload,\nwaits 30s for PDF generation, then sends\nthe invoice to the customer via Pennylane.\n\nEndpoint: POST /customer_invoices/{id}/send_by_email\n\nA 409 means the PDF is not ready yet.",
+ "height": 624,
+ "width": 672,
+ "color": 4
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 2000,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "53f73bb0-8e6f-4f4d-bf16-89af06cfa1bc",
+ "name": "Sticky Note5"
+ },
+ {
+ "parameters": {
+ "content": "## 6. Notifications\n\nSlack: always sends a summary to your channel.\nGmail: sends an HTML notification if\nnotification_email is provided in the payload.\n\nBoth are optional. Remove or replace\nwith your preferred channel (Telegram, etc.).",
+ "height": 624,
+ "width": 800,
+ "color": 4
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 2672,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "83320de8-8149-4bc1-b58d-c20c20b7b549",
+ "name": "Sticky Note6"
+ },
+ {
+ "parameters": {
+ "content": "## 7. Webhook response\n\nReturns a JSON response to the caller with:\n- success: true/false\n- invoice_id\n- invoice_number\n- amount\n- customer\n- public_url (link to PDF)\n\nConfigure Error Workflow in Settings\nfor production error handling.",
+ "height": 624,
+ "width": 368,
+ "color": 5
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 3472,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "bfbe45e4-cacc-4784-a309-b378ed99a2ce",
+ "name": "Sticky Note7"
+ }
+ ],
+ "pinData": {},
+ "connections": {
+ "WH Receive Invoice Data": {
+ "main": [
+ [
+ {
+ "node": "Code Validate Payload",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Code Validate Payload": {
+ "main": [
+ [
+ {
+ "node": "PL Search Customer",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "PL Search Customer": {
+ "main": [
+ [
+ {
+ "node": "IF Customer Exists",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "IF Customer Exists": {
+ "main": [
+ [
+ {
+ "node": "Set Customer ID",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "PL Create Customer",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "PL Create Customer": {
+ "main": [
+ [
+ {
+ "node": "Set Customer ID",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Set Customer ID": {
+ "main": [
+ [
+ {
+ "node": "Code Build Invoice Payload",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Code Build Invoice Payload": {
+ "main": [
+ [
+ {
+ "node": "PL Create Invoice",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "PL Create Invoice": {
+ "main": [
+ [
+ {
+ "node": "IF Send Email",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "IF Send Email": {
+ "main": [
+ [
+ {
+ "node": "Wait PDF Generation",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "Code Build Notification",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Code Build Notification": {
+ "main": [
+ [
+ {
+ "node": "SL Send Notification",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "IF Has Notification Email",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "SL Send Notification": {
+ "main": [
+ [
+ {
+ "node": "Set Output Response",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Wait PDF Generation": {
+ "main": [
+ [
+ {
+ "node": "PL Send Invoice Email",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "PL Send Invoice Email": {
+ "main": [
+ [
+ {
+ "node": "Code Build Notification",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "GM Send Notification": {
+ "main": [
+ [
+ {
+ "node": "Set Output Response",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "IF Has Notification Email": {
+ "main": [
+ [
+ {
+ "node": "GM Send Notification",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "Set Output Response",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": false,
+ "settings": {
+ "executionOrder": "v1",
+ "binaryMode": "separate",
+ "timeSavedMode": "fixed",
+ "callerPolicy": "workflowsFromSameOwner",
+ "availableInMCP": true,
+ "timeSavedPerExecution": 5
+ },
+ "versionId": "91238c54-efda-4f81-b8d0-6b2fee1f78e8",
+ "meta": {
+ "templateCredsSetupCompleted": true,
+ "instanceId": "70ec3bed341af97283a0a6dbf0b48c20f5e82d1a117b40451ad8016f7992090c"
+ },
+ "id": "UI00dYBcdYiWLc5Y",
+ "tags": []
+}
diff --git a/Invoice_Processing/[PENNYLANE] Overdue Reminder.json b/Invoice_Processing/[PENNYLANE] Overdue Reminder.json
new file mode 100644
index 0000000..e9391ce
--- /dev/null
+++ b/Invoice_Processing/[PENNYLANE] Overdue Reminder.json
@@ -0,0 +1,331 @@
+{
+ "name": "[PENNYLANE] Overdue Reminder",
+ "nodes": [
+ {
+ "parameters": {
+ "content": "# [PENNYLANE] Overdue Reminder\n\n**Trigger:** Schedule (daily at 9:00 AM)\n**Source:** Pennylane API v2 - customer invoices\n\n---\n\n### Flow\n\n`Schedule Trigger`\n → `PL Fetch Invoices`\n → `Code Filter Overdue`\n → `IF Has Overdue`\n → ✅ `Code Build Reminder`\n → `SL Send Notification`\n → `Set Done`\n\n---\n\n### Services & Credentials\n\n| Service | Credential |\n|---------|-----------|\n| Pennylane API v2 | Pennylane API (HTTP Header Auth) |\n| Slack (optional) | Slack OAuth |\n\n---\n\n### Node refs for $()\n\n- Schedule Trigger\n- PL Fetch Invoices\n- Code Filter Overdue\n- IF Has Overdue\n- Code Build Reminder\n- SL Send Notification\n- Set Done",
+ "height": 608,
+ "width": 416
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ -448,
+ -80
+ ],
+ "typeVersion": 1,
+ "id": "7709720d-5bd0-419b-9a73-d63f4baf97fe",
+ "name": "Sticky Note"
+ },
+ {
+ "parameters": {
+ "rule": {
+ "interval": [
+ {
+ "triggerAtHour": 9
+ }
+ ]
+ }
+ },
+ "type": "n8n-nodes-base.scheduleTrigger",
+ "typeVersion": 1.3,
+ "position": [
+ 80,
+ 224
+ ],
+ "id": "7c7a9506-5ec1-460d-a014-7d6db37c3a35",
+ "name": "Schedule Trigger"
+ },
+ {
+ "parameters": {
+ "url": "https://app.pennylane.com/api/external/v2/customer_invoices",
+ "authentication": "genericCredentialType",
+ "genericAuthType": "httpHeaderAuth",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 320,
+ 224
+ ],
+ "id": "bf00125c-bc60-4c7e-8649-bf3d9faa2f42",
+ "name": "PL Fetch Invoices",
+ "credentials": {
+ "httpHeaderAuth": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "jsCode": "// Refs: 'PL Fetch Invoices'\nconst invoices = $input.first().json.items;\nconst today = new Date().toISOString().split('T')[0];\nconst THRESHOLD_DAYS = 7;\n\nconst overdue = [];\n\nfor (const inv of invoices) {\n if (inv.draft || inv.paid) continue;\n if (inv.deadline >= today) continue;\n\n const deadlineDate = new Date(inv.deadline);\n const todayDate = new Date(today);\n const daysOverdue = Math.floor((todayDate - deadlineDate) / (1000 * 60 * 60 * 24));\n\n if (daysOverdue >= THRESHOLD_DAYS) {\n overdue.push({\n id: inv.id,\n invoice_number: inv.invoice_number,\n label: inv.label,\n currency_amount: inv.currency_amount,\n currency: inv.currency,\n deadline: inv.deadline,\n days_overdue: daysOverdue,\n customer_id: inv.customer.id,\n public_file_url: inv.public_file_url\n });\n }\n}\n\nreturn [{\n json: {\n overdue_count: overdue.length,\n overdue,\n has_overdue: overdue.length > 0\n }\n}];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 560,
+ 224
+ ],
+ "id": "d25af660-3af7-4869-b149-7b9909ffbc0f",
+ "name": "Code Filter Overdue"
+ },
+ {
+ "parameters": {
+ "conditions": {
+ "options": {
+ "caseSensitive": true,
+ "leftValue": "",
+ "typeValidation": "strict",
+ "version": 3
+ },
+ "conditions": [
+ {
+ "id": "1a032dfe-0694-4d96-8c75-c891a86339b2",
+ "leftValue": "={{ $json.has_overdue }}",
+ "rightValue": "",
+ "operator": {
+ "type": "boolean",
+ "operation": "true",
+ "singleValue": true
+ }
+ }
+ ],
+ "combinator": "and"
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.if",
+ "typeVersion": 2.3,
+ "position": [
+ 736,
+ 224
+ ],
+ "id": "28c82e3c-1d77-4137-8396-3d79ef392cd8",
+ "name": "IF Has Overdue"
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "7c77345e-ab79-4816-aa14-d62c9e9b61a9",
+ "name": "status",
+ "value": "no overdue invoices",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [
+ 1552,
+ 240
+ ],
+ "id": "00fca357-c089-4a78-a7eb-9bbf9a1f76b9",
+ "name": "Set Done"
+ },
+ {
+ "parameters": {
+ "jsCode": "// Refs: 'Code Filter Overdue'\nconst data = $input.first().json;\nconst lines = ['🚨 Overdue Invoice Reminder', ''];\n\nlines.push(`${data.overdue_count} invoice(s) require attention:`);\nlines.push('');\n\nfor (const inv of data.overdue) {\n lines.push(`• ${inv.invoice_number} | ${inv.currency_amount} ${inv.currency}`);\n lines.push(` Due: ${inv.deadline} (${inv.days_overdue} days overdue)`);\n lines.push(` ${inv.public_file_url}`);\n lines.push('');\n}\n\nreturn [{\n json: {\n message: lines.join('\\n'),\n overdue_count: data.overdue_count\n }\n}];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 976,
+ 144
+ ],
+ "id": "73f0f390-b353-487b-ad7e-98a87ca7d4ca",
+ "name": "Code Build Reminder"
+ },
+ {
+ "parameters": {
+ "authentication": "oAuth2",
+ "select": "channel",
+ "channelId": {
+ "__rl": true,
+ "value": "",
+ "mode": "list",
+ "cachedResultName": ""
+ },
+ "text": "={{ $json.message }}",
+ "otherOptions": {}
+ },
+ "type": "n8n-nodes-base.slack",
+ "typeVersion": 2.4,
+ "position": [
+ 1168,
+ 144
+ ],
+ "id": "04edc672-c812-4b77-8643-d68f58bbc2c1",
+ "name": "SL Send Notification",
+ "webhookId": "6355e179-fd0a-4eed-8311-43954f4330ea",
+ "credentials": {
+ "slackOAuth2Api": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "content": "## 1. Trigger\n\nRuns once daily at 9:00 AM.\nChecks for overdue invoices that need\nfollow-up.\n\nAdjust schedule in the Schedule Trigger\nnode to fit your needs.",
+ "height": 480,
+ "color": 5
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 16,
+ -80
+ ],
+ "typeVersion": 1,
+ "id": "4ca9d854-033b-49bf-bd18-7fac67c6e674",
+ "name": "Sticky Note1"
+ },
+ {
+ "parameters": {
+ "content": "## 2. Fetch invoices\n\nRetrieves all customer invoices from Pennylane.\n\nEndpoint: GET /customer_invoices\n\nSame approach as WF2: no server-side\nstatus filtering available, all logic\nis handled in the Code node.",
+ "height": 480,
+ "color": 3
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 256,
+ -80
+ ],
+ "typeVersion": 1,
+ "id": "000b18e0-894f-4d7a-aeaa-efd9b9106545",
+ "name": "Sticky Note2"
+ },
+ {
+ "parameters": {
+ "content": "## 3. Filter overdue\n\nIdentifies unpaid invoices past their\ndeadline by at least 7 days (THRESHOLD_DAYS).\n\nAdjust the threshold in Code Filter Overdue\nto change the sensitivity.\n\nSkips draft and paid invoices.\nCalculates exact days overdue for each.",
+ "height": 480,
+ "width": 432,
+ "color": 3
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 496,
+ -80
+ ],
+ "typeVersion": 1,
+ "id": "ba078fad-ae3c-4ebb-b045-384adf189fa5",
+ "name": "Sticky Note3"
+ },
+ {
+ "parameters": {
+ "content": "## 4. Notify\n\nBuilds a detailed reminder with:\n- Invoice number and amount\n- Due date and days overdue\n- Direct link to the invoice PDF\n\nSends to Slack. Replace with your preferred\nnotification channel (Telegram, Email, etc.).",
+ "height": 480,
+ "width": 480,
+ "color": 4
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 928,
+ -80
+ ],
+ "typeVersion": 1,
+ "id": "c4d123ec-9c7d-41de-b22a-e93380e7e64a",
+ "name": "Sticky Note4"
+ },
+ {
+ "parameters": {
+ "content": "## 5. Done\n\nNo overdue invoices detected.\nWorkflow ends silently.\n\nThis node ensures a clean execution log\nin n8n. No notification is sent.",
+ "height": 480,
+ "width": 368,
+ "color": 4
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 1408,
+ -80
+ ],
+ "typeVersion": 1,
+ "id": "a8a9b804-edbb-4123-beec-1f937e50eee6",
+ "name": "Sticky Note5"
+ }
+ ],
+ "pinData": {},
+ "connections": {
+ "Schedule Trigger": {
+ "main": [
+ [
+ {
+ "node": "PL Fetch Invoices",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "PL Fetch Invoices": {
+ "main": [
+ [
+ {
+ "node": "Code Filter Overdue",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Code Filter Overdue": {
+ "main": [
+ [
+ {
+ "node": "IF Has Overdue",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "IF Has Overdue": {
+ "main": [
+ [
+ {
+ "node": "Code Build Reminder",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "Set Done",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Code Build Reminder": {
+ "main": [
+ [
+ {
+ "node": "SL Send Notification",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": false,
+ "settings": {
+ "executionOrder": "v1",
+ "binaryMode": "separate",
+ "timeSavedMode": "fixed",
+ "callerPolicy": "workflowsFromSameOwner",
+ "availableInMCP": true
+ },
+ "versionId": "a2cf0709-0d9b-4746-a6ca-ed2dadb03ada",
+ "meta": {
+ "templateCredsSetupCompleted": true,
+ "instanceId": "70ec3bed341af97283a0a6dbf0b48c20f5e82d1a117b40451ad8016f7992090c"
+ },
+ "id": "hihBb5dvnUpjT1Ti",
+ "tags": []
+}
diff --git a/Invoice_Processing/[PENNYLANE] Track Payments.json b/Invoice_Processing/[PENNYLANE] Track Payments.json
new file mode 100644
index 0000000..c59048a
--- /dev/null
+++ b/Invoice_Processing/[PENNYLANE] Track Payments.json
@@ -0,0 +1,340 @@
+{
+ "name": "[PENNYLANE] Track Payments",
+ "nodes": [
+ {
+ "parameters": {
+ "content": "# [PENNYLANE] Track Payments\n\n**Trigger:** Schedule (every 15 minutes)\n**Source:** Pennylane API v2 - customer invoices\n\n---\n\n### Flow\n\n`Schedule Trigger`\n → `PL Fetch Invoices`\n → `Code Filter Status Changes`\n → `IF Has Updates`\n → ✅ `Code Build Payment Notification`\n → `SL Send Notification`\n → `Set Done`\n\n---\n\n### Services & Credentials\n\n| Service | Credential |\n|---------|-----------|\n| Pennylane API v2 | Pennylane API (HTTP Header Auth) |\n| Slack (optional) | Slack OAuth |\n\n---\n\n### Node refs for $()\n\n- Schedule Trigger\n- PL Fetch Invoices\n- Code Filter Status Changes\n- IF Has Updates\n- Code Build Payment Notification\n- SL Send Notification\n- Set Done",
+ "height": 608,
+ "width": 544
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ -688,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "6ea2a45d-df0a-4ec7-a38e-740c40782606",
+ "name": "Sticky Note"
+ },
+ {
+ "parameters": {
+ "rule": {
+ "interval": [
+ {
+ "field": "minutes",
+ "minutesInterval": 15
+ }
+ ]
+ }
+ },
+ "type": "n8n-nodes-base.scheduleTrigger",
+ "typeVersion": 1.3,
+ "position": [
+ -16,
+ 32
+ ],
+ "id": "d45398dc-a8b8-4c63-a872-ea829ecfc92c",
+ "name": "Schedule Trigger"
+ },
+ {
+ "parameters": {
+ "url": "https://app.pennylane.com/api/external/v2/customer_invoices",
+ "authentication": "genericCredentialType",
+ "genericAuthType": "httpHeaderAuth",
+ "sendQuery": true,
+ "queryParameters": {
+ "parameters": []
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 256,
+ 32
+ ],
+ "id": "3472cf87-8215-4bd0-9e6d-fa78ecaed39a",
+ "name": "PL Fetch Invoices",
+ "credentials": {
+ "httpHeaderAuth": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "jsCode": "// Refs: 'PL Fetch Invoices'\nconst invoices = $input.first().json.items;\nconst today = new Date().toISOString().split('T')[0];\n\nconst results = {\n paid: [],\n overdue: [],\n upcoming: []\n};\n\nfor (const inv of invoices) {\n if (inv.draft) continue;\n\n if (inv.paid) {\n results.paid.push(inv);\n } else if (inv.deadline < today) {\n results.overdue.push(inv);\n } else {\n results.upcoming.push(inv);\n }\n}\n\nreturn [{\n json: {\n total_invoices: invoices.length,\n paid_count: results.paid.length,\n overdue_count: results.overdue.length,\n upcoming_count: results.upcoming.length,\n paid: results.paid,\n overdue: results.overdue,\n has_updates: results.paid.length > 0 || results.overdue.length > 0\n }\n}];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 496,
+ 32
+ ],
+ "id": "d2b7e251-96c3-4228-b08c-2c147dcda8b7",
+ "name": "Code Filter Status Changes"
+ },
+ {
+ "parameters": {
+ "conditions": {
+ "options": {
+ "caseSensitive": true,
+ "leftValue": "",
+ "typeValidation": "loose",
+ "version": 3
+ },
+ "conditions": [
+ {
+ "id": "f6d3fe78-bb28-4193-b4c0-7d04e963d48f",
+ "leftValue": "={{ $json.has_updates }}",
+ "rightValue": "",
+ "operator": {
+ "type": "boolean",
+ "operation": "true",
+ "singleValue": true
+ }
+ }
+ ],
+ "combinator": "and"
+ },
+ "looseTypeValidation": true,
+ "options": {}
+ },
+ "type": "n8n-nodes-base.if",
+ "typeVersion": 2.3,
+ "position": [
+ 672,
+ 32
+ ],
+ "id": "023f3a34-1a30-4ab2-b8f4-14227dac655c",
+ "name": "IF Has Updates"
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "aee99e96-e05f-4770-b8c4-53a574358d7b",
+ "name": "Status",
+ "value": "no updates",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [
+ 1504,
+ 48
+ ],
+ "id": "ac4d9578-3a5b-43cc-ab0a-8673987a377a",
+ "name": "Set Done"
+ },
+ {
+ "parameters": {
+ "jsCode": "// Refs: 'Code Filter Status Changes'\nconst data = $input.first().json;\nconst lines = ['📊 Pennylane Invoice Status Report', ''];\n\nif (data.paid_count > 0) {\n lines.push(`✅ ${data.paid_count} invoice(s) paid:`);\n for (const inv of data.paid) {\n lines.push(` - ${inv.invoice_number} | ${inv.currency_amount} ${inv.currency} | ${inv.label}`);\n }\n lines.push('');\n}\n\nif (data.overdue_count > 0) {\n lines.push(`⚠️ ${data.overdue_count} invoice(s) overdue:`);\n for (const inv of data.overdue) {\n lines.push(` - ${inv.invoice_number} | ${inv.currency_amount} ${inv.currency} | Due: ${inv.deadline} | ${inv.label}`);\n }\n lines.push('');\n}\n\nlines.push(`📋 ${data.upcoming_count} invoice(s) upcoming`);\nlines.push(`Total tracked: ${data.total_invoices}`);\n\nreturn [{\n json: {\n message: lines.join('\\n'),\n paid_count: data.paid_count,\n overdue_count: data.overdue_count,\n upcoming_count: data.upcoming_count\n }\n}];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 912,
+ -48
+ ],
+ "id": "66da8691-e7a6-4c3d-92f6-701fa2fdcc18",
+ "name": "Code Build Payment Notification"
+ },
+ {
+ "parameters": {
+ "authentication": "oAuth2",
+ "select": "channel",
+ "channelId": {
+ "__rl": true,
+ "value": "",
+ "mode": "list",
+ "cachedResultName": ""
+ },
+ "text": "={{ $json.message }}",
+ "otherOptions": {}
+ },
+ "type": "n8n-nodes-base.slack",
+ "typeVersion": 2.4,
+ "position": [
+ 1152,
+ -48
+ ],
+ "id": "8284d459-6b14-428c-84c2-208511b97546",
+ "name": "SL Send Notification",
+ "webhookId": "763cb5da-2c6f-4311-9c3c-a652d891af6e",
+ "credentials": {
+ "slackOAuth2Api": {
+ "id": "",
+ "name": ""
+ }
+ }
+ },
+ {
+ "parameters": {
+ "content": "## 1. Trigger\n\nRuns every 15 minutes.\nPolls Pennylane for all customer invoices.\n\nAdjust the schedule interval in the\nSchedule Trigger node to fit your needs.",
+ "height": 560,
+ "width": 272,
+ "color": 6
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ -112,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "8110087c-5802-4805-bedb-151a9261bb02",
+ "name": "Sticky Note1"
+ },
+ {
+ "parameters": {
+ "content": "## 2. Fetch invoices\n\nRetrieves all customer invoices from Pennylane.\n\nEndpoint: GET /customer_invoices\n\nNote: the Pennylane API does not support\nfiltering by status or paid fields.\nAll filtering is done in the next Code node.",
+ "height": 560,
+ "width": 272,
+ "color": 3
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 160,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "07df7bfc-ded9-4119-b697-706ed1710194",
+ "name": "Sticky Note2"
+ },
+ {
+ "parameters": {
+ "content": "## 3. Classify & filter\n\nClassifies each invoice into 3 categories:\n- paid: invoice has been settled\n- overdue: past deadline and still unpaid\n- upcoming: not yet due\n\nOnly triggers a notification if there are\npaid or overdue invoices (has_updates = true).\n\nIf all invoices are upcoming, the workflow\nends silently via Set Done.",
+ "height": 560,
+ "width": 416,
+ "color": 3
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 432,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "f4e10525-59ed-46a2-ad6b-1b2b8e4bb789",
+ "name": "Sticky Note3"
+ },
+ {
+ "parameters": {
+ "content": "## 4. Notify\n\nBuilds a summary message with:\n- List of newly paid invoices\n- List of overdue invoices\n- Count of upcoming invoices\n\nSends to Slack. Replace with your preferred\nnotification channel (Telegram, Email, etc.).",
+ "height": 560,
+ "width": 512,
+ "color": 4
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 848,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "ee1f614f-fc39-4d68-b1e6-74dc303b0297",
+ "name": "Sticky Note4"
+ },
+ {
+ "parameters": {
+ "content": "## 5. Done\n\nNo paid or overdue invoices detected.\nWorkflow ends silently.\n\nThis node ensures a clean execution log\nin n8n. No notification is sent.",
+ "height": 560,
+ "width": 368,
+ "color": 4
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "position": [
+ 1360,
+ -272
+ ],
+ "typeVersion": 1,
+ "id": "4f5a9ddb-f537-4282-8c6a-47a3b36f30b5",
+ "name": "Sticky Note5"
+ }
+ ],
+ "pinData": {},
+ "connections": {
+ "Schedule Trigger": {
+ "main": [
+ [
+ {
+ "node": "PL Fetch Invoices",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "PL Fetch Invoices": {
+ "main": [
+ [
+ {
+ "node": "Code Filter Status Changes",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Code Filter Status Changes": {
+ "main": [
+ [
+ {
+ "node": "IF Has Updates",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "IF Has Updates": {
+ "main": [
+ [
+ {
+ "node": "Code Build Payment Notification",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "Set Done",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Code Build Payment Notification": {
+ "main": [
+ [
+ {
+ "node": "SL Send Notification",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": false,
+ "settings": {
+ "executionOrder": "v1",
+ "binaryMode": "separate",
+ "timeSavedMode": "fixed",
+ "callerPolicy": "workflowsFromSameOwner",
+ "executionTimeout": -1,
+ "availableInMCP": true
+ },
+ "versionId": "b182dbdb-49af-4e9b-abb5-964fbb5f6171",
+ "meta": {
+ "templateCredsSetupCompleted": true,
+ "instanceId": "70ec3bed341af97283a0a6dbf0b48c20f5e82d1a117b40451ad8016f7992090c"
+ },
+ "id": "iiAZtgzpKqekwA9W",
+ "tags": []
+}