AutomationFlowsSlack & Telegram › Track Pennylane Invoice Payment Status with Slack Notifications

Track Pennylane Invoice Payment Status with Slack Notifications

ByGauthier Huguenin @gauthier-huguenin on n8n.io

Teams using Pennylane (French accounting platform) who want automatic alerts when invoices are paid or become overdue, without checking Pennylane manually.

Cron / scheduled trigger★★★★☆ complexity13 nodesHTTP RequestSlack
Slack & Telegram Trigger: Cron / scheduled Nodes: 13 Complexity: ★★★★☆ Added:

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

This workflow follows the HTTP Request → Slack 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
{
  "name": "Track Pennylane invoice payment status with Slack notifications",
  "nodes": [
    {
      "id": "6ea2a45d-df0a-4ec7-a38e-740c40782606",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -784,
        -272
      ],
      "parameters": {
        "width": 640,
        "height": 1024,
        "content": "# Track Pennylane invoice payment status with Slack notifications\n\n## Who is this for\n\nTeams using Pennylane who want automatic alerts when invoices are paid or become overdue, without checking Pennylane manually.\n\n## What it does\n\nThis workflow runs every 15 minutes, fetches all customer invoices from Pennylane, and classifies them as paid, overdue, or upcoming. If any invoices have been paid or are overdue, it sends a summary notification to Slack. If everything is normal, the workflow ends silently.\n\n## How to set up\n\n1. Import this workflow into n8n\n2. Create a Header Auth credential: name `Authorization`, value `Bearer <YOUR_PENNYLANE_TOKEN>`\n3. Select your Slack channel in the SL Send Notification node\n4. Adjust the schedule interval in the Schedule Trigger node if needed\n\n## Requirements\n\n- Pennylane account with API access (Essentiel plan or higher)\n- Pennylane API token with scope: customer_invoices:all\n- (Optional) Slack workspace\n\nNote: Pennylane API does not support filtering by status or paid fields. All classification is done client-side in a Code node.\n\nPart of a 3-workflow billing suite: https://github.com/Gauthier-Huguenin/n8n-pennylane-auto-invoicing\n\n---\n\n### Flow\n\n`Schedule Trigger`\n  \u2192 `PL Fetch Invoices`\n  \u2192 `Code Filter Status Changes`\n  \u2192 `IF Has Updates`\n    \u2192 \u2705 `Code Build Payment Notification` \u2192 `SL Send Notification`\n    \u2192 \u274c `Set Done`\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"
      },
      "typeVersion": 1
    },
    {
      "id": "d45398dc-a8b8-4c63-a872-ea829ecfc92c",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -16,
        32
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "3472cf87-8215-4bd0-9e6d-fa78ecaed39a",
      "name": "PL Fetch Invoices",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        256,
        32
      ],
      "parameters": {
        "url": "https://app.pennylane.com/api/external/v2/customer_invoices",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": []
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "d2b7e251-96c3-4228-b08c-2c147dcda8b7",
      "name": "Code Filter Status Changes",
      "type": "n8n-nodes-base.code",
      "position": [
        496,
        32
      ],
      "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}];"
      },
      "typeVersion": 2
    },
    {
      "id": "023f3a34-1a30-4ab2-b8f4-14227dac655c",
      "name": "IF Has Updates",
      "type": "n8n-nodes-base.if",
      "position": [
        672,
        32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f6d3fe78-bb28-4193-b4c0-7d04e963d48f",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.has_updates }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "ac4d9578-3a5b-43cc-ab0a-8673987a377a",
      "name": "Set Done",
      "type": "n8n-nodes-base.set",
      "position": [
        1504,
        48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "aee99e96-e05f-4770-b8c4-53a574358d7b",
              "name": "Status",
              "type": "string",
              "value": "no updates"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "66da8691-e7a6-4c3d-92f6-701fa2fdcc18",
      "name": "Code Build Payment Notification",
      "type": "n8n-nodes-base.code",
      "position": [
        912,
        -48
      ],
      "parameters": {
        "jsCode": "// Refs: 'Code Filter Status Changes'\nconst data = $input.first().json;\nconst lines = ['\ud83d\udcca Pennylane Invoice Status Report', ''];\n\nif (data.paid_count > 0) {\n  lines.push(`\u2705 ${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(`\u26a0\ufe0f ${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(`\ud83d\udccb ${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}];"
      },
      "typeVersion": 2
    },
    {
      "id": "8284d459-6b14-428c-84c2-208511b97546",
      "name": "SL Send Notification",
      "type": "n8n-nodes-base.slack",
      "position": [
        1152,
        -48
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": ""
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "8110087c-5802-4805-bedb-151a9261bb02",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -112,
        -272
      ],
      "parameters": {
        "color": 6,
        "width": 272,
        "height": 560,
        "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."
      },
      "typeVersion": 1
    },
    {
      "id": "07df7bfc-ded9-4119-b697-706ed1710194",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        -272
      ],
      "parameters": {
        "color": 3,
        "width": 272,
        "height": 560,
        "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."
      },
      "typeVersion": 1
    },
    {
      "id": "f4e10525-59ed-46a2-ad6b-1b2b8e4bb789",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        -272
      ],
      "parameters": {
        "color": 3,
        "width": 416,
        "height": 560,
        "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."
      },
      "typeVersion": 1
    },
    {
      "id": "ee1f614f-fc39-4d68-b1e6-74dc303b0297",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        848,
        -272
      ],
      "parameters": {
        "color": 4,
        "width": 512,
        "height": 560,
        "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.)."
      },
      "typeVersion": 1
    },
    {
      "id": "4f5a9ddb-f537-4282-8c6a-47a3b36f30b5",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        -272
      ],
      "parameters": {
        "color": 4,
        "width": 368,
        "height": 560,
        "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."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "callerPolicy": "workflowsFromSameOwner",
    "timeSavedMode": "fixed",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "connections": {
    "IF Has Updates": {
      "main": [
        [
          {
            "node": "Code Build Payment Notification",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set Done",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "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
          }
        ]
      ]
    },
    "Code Build Payment Notification": {
      "main": [
        [
          {
            "node": "SL Send Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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

Teams using Pennylane (French accounting platform) who want automatic alerts when invoices are paid or become overdue, without checking Pennylane manually.

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

More Slack & Telegram workflows → · Browse all categories →

Related workflows

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

Slack & Telegram

This workflow is an automated employee time tracking and reporting system that monitors weekly work hours via TMetric, then delivers personalized summaries directly to each team member on Slack. It co

HTTP Request, Item Lists, Data Table +1
Slack & Telegram

Import Productboard Notes Companies And Features Into Snowflake. Uses stickyNote, httpRequest, splitOut, snowflake. Scheduled trigger; 35 nodes.

HTTP Request, Snowflake, Slack
Slack & Telegram

Import Productboard Notes, Companies and Features into Snowflake. Uses stickyNote, httpRequest, splitOut, snowflake. Scheduled trigger; 35 nodes.

HTTP Request, Snowflake, Slack
Slack & Telegram

This workflow imports Productboard data into Snowflake, automating data extraction, mapping, and updates for features, companies, and notes. It supports scheduled weekly updates, data cleansing, and S

HTTP Request, Snowflake, Slack
Slack & Telegram

This workflow streamlines the entire inventory replenishment process by leveraging AI for demand forecasting and intelligent logic for supplier selection. It aggregates data from multiple sources—POS

HTTP Request, MySQL, Slack