{
  "id": "dzUhWkfShIkWdOzp",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Daily Report Generator with Mattermost and HubSpot",
  "tags": [],
  "nodes": [
    {
      "id": "0962a345-2873-414a-8c21-2c8ca0b39f98",
      "name": "\ud83d\udcdd Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1024,
        -48
      ],
      "parameters": {
        "width": 550,
        "height": 738,
        "content": "# Daily Report Generator\n\n## How it works\nThis workflow is triggered via an authenticated webhook and automatically downloads the latest sales and support statistics from two external REST APIs. Both datasets are validated, merged, and summarized inside n8n. A lightweight PDF summary is generated on-the-fly to provide a portable snapshot of key business metrics. The finished report is uploaded to HubSpot so that it is available to the entire revenue team directly inside the CRM. Finally, a formatted message with quick stats and a link to the stored file is posted to the Mattermost channel configured during setup. Should any API call or storage step fail, the workflow raises an immediate error alert in the same channel and returns a JSON error to the original webhook caller.\n\n## Setup steps\n1. Create API credentials for your Sales and Support endpoints and add them to the HTTP Request nodes.\n2. Configure the HubSpot credential in the \u201cUpload Report to HubSpot\u201d node.\n3. Add a Mattermost credential to the \u201cSend Mattermost Notification\u201d node.\n4. Update the `salesEndpoint`, `supportEndpoint`, and `mattermostChannel` values in the \u201cPrepare Config\u201d node.\n5. Deploy the workflow and copy the unique webhook URL.\n6. Schedule or call the webhook daily from your scheduler or external system.\n7. Test the flow; confirm that HubSpot receives a file and Mattermost displays the summary."
      },
      "typeVersion": 1
    },
    {
      "id": "ef1364ab-8239-4ee7-af9b-aeb85a1432e6",
      "name": "\u2699\ufe0f Trigger & Config",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 450,
        "height": 798,
        "content": "## Trigger & Configuration\nThis group contains the Webhook Trigger that starts the workflow and the configuration Set node. The trigger accepts a simple POST request\u2014no payload required unless you want to pass in a custom date or override parameters. Immediately after firing, the \u201cPrepare Config\u201d node injects dynamic values such as the reporting date, the REST API endpoints, and the target Mattermost channel. Adjust these fields to point at your own services. You can also include API query parameters (for example, date filters) directly in the URL strings; expressions are supported so you can keep everything fully dynamic. Keeping these variables in one place makes the solution easy to maintain and allows non-technical teammates to update endpoints without touching the rest of the logic. Both data-collection branches downstream rely on these values, so any mistakes here will cascade\u2014always test with the \u201cExecute Node\u201d button before going live."
      },
      "typeVersion": 1
    },
    {
      "id": "59b24194-f5f7-46da-af54-81229e628380",
      "name": "\ud83d\udce1 Data Collection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        304,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 450,
        "height": 798,
        "content": "## Data Collection\nThe two HTTP Request nodes run in parallel and retrieve daily statistics from your external systems. One call fetches yesterday\u2019s closed-won revenue while the second pulls fresh support ticket counts. Each request includes built-in authentication via n8n credentials so that no secrets are ever exposed inside the flow. The subsequent IF nodes validate that the remote service responded successfully by checking the status code. When the response is anything other than 200, the item is routed to the error-handling branch so that an alert is fired and the caller receives immediate feedback. By isolating this logic we ensure that a failure in one data source does not silently corrupt the final report. If both calls succeed, the results are passed to the Merge node, where they are combined into a single item ready for aggregation."
      },
      "typeVersion": 1
    },
    {
      "id": "889fc62a-78d1-46aa-825f-ba5641ed7a8c",
      "name": "\ud83d\udee0\ufe0f Data Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        752,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 594,
        "height": 622,
        "content": "## Processing & PDF Generation\nAfter both datasets have been merged, the \u201cAggregate Metrics\u201d Code node calculates the key performance indicators required by leadership\u2014total revenue, open ticket count, and any other custom metrics you choose to add. The data is now in a compact structure that subsequent nodes can use. The \u201cGenerate PDF\u201d node converts this JSON summary into a plain-text PDF so that it can be stored or emailed as a self-contained artifact. The PDF is rendered on-the-fly using Node.js Buffer operations and attached to the item as binary data, keeping the workflow free from external dependencies such as third-party rendering services. The resulting item now contains both machine-readable metrics and a human-friendly document."
      },
      "typeVersion": 1
    },
    {
      "id": "86fa47c6-6980-4d83-a386-c985a4d7956b",
      "name": "\ud83d\udcbe Storage & Notification",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1344,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 978,
        "height": 622,
        "content": "## Storage & Notifications\nWith the PDF generated, the workflow uploads the file to HubSpot using the built-in File endpoint. Storing reports inside the CRM keeps historical documentation next to the deals and contacts that generated the numbers. The following IF node validates the upload and branches accordingly. On success, the \u201cCraft Mattermost Message\u201d node assembles a Markdown string that includes the headline metrics and the HubSpot file ID or public URL. That message is then posted to a designated Mattermost channel, ensuring that the whole team receives the update without checking their inbox. If the upload fails, execution is redirected to the error-handling section so that an immediate alert is still delivered."
      },
      "typeVersion": 1
    },
    {
      "id": "47baaa6a-d357-480c-b546-7de38e915223",
      "name": "\ud83d\udea8 Error Handling",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 626,
        "height": 446,
        "content": "## Error Handling\nAll failure paths converge in this section. Whenever an upstream IF node flags a problem\u2014whether an HTTP request returns a non-200 status or HubSpot rejects the file\u2014the item is routed here. The \u201cCompose Error Message\u201d code node builds a concise summary that includes the node that failed and a truncated version of the returned error message to avoid flooding the chat. The alert is posted to the same Mattermost channel via the dedicated error node so that your team can act quickly. Finally, the workflow responds to the original webhook caller with the error details and a 200 status, preventing timeouts on the calling system while still exposing what went wrong."
      },
      "typeVersion": 1
    },
    {
      "id": "20f01a9a-728a-4625-a405-a593fa98d95a",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -48,
        352
      ],
      "parameters": {
        "path": "daily-report",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1
    },
    {
      "id": "854477ac-e453-4e51-8bed-734df7b89f90",
      "name": "Prepare Config",
      "type": "n8n-nodes-base.set",
      "position": [
        160,
        352
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "01483636-39ce-43f9-9a0d-cb2c960c1eae",
      "name": "Fetch Sales Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        368,
        240
      ],
      "parameters": {
        "url": "={{ $json.salesEndpoint }}",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "c3eb0baf-478d-4a68-a7ec-84fca55bc363",
      "name": "Sales Data OK?",
      "type": "n8n-nodes-base.if",
      "position": [
        560,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "number": [
            {
              "value1": "={{ $json.statusCode || 200 }}",
              "value2": 200,
              "operation": "equal"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3353804f-1448-45bf-91b2-8a81e348bd40",
      "name": "Fetch Support Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        368,
        480
      ],
      "parameters": {
        "url": "={{ $json.supportEndpoint }}",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "1443f01f-0f66-400b-a73a-5cf8985a8465",
      "name": "Support Data OK?",
      "type": "n8n-nodes-base.if",
      "position": [
        560,
        480
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "number": [
            {
              "value1": "={{ $json.statusCode || 200 }}",
              "value2": 200,
              "operation": "equal"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2e31d96a-f805-49cd-8913-087c5bf4fefd",
      "name": "Combine Results",
      "type": "n8n-nodes-base.merge",
      "position": [
        768,
        352
      ],
      "parameters": {
        "mode": "mergeByIndex",
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "2aa7ee13-6e8e-495e-b5b0-c91705f4124a",
      "name": "Aggregate Metrics",
      "type": "n8n-nodes-base.code",
      "position": [
        960,
        352
      ],
      "parameters": {
        "jsCode": "const salesData = items[0].json;\nconst supportData = items[1].json;\n\nconst totalRevenue = salesData.totalRevenue || salesData.total_revenue || 0;\nconst ticketsOpen  = supportData.open_tickets || supportData.ticketsOpen || 0;\n\nreturn [\n  {\n    json: {\n      reportDate: new Date().toISOString().slice(0,10),\n      totalRevenue,\n      ticketsOpen,\n      salesData,\n      supportData\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "8efba282-6729-45cc-8b37-fd64f98f065b",
      "name": "Generate PDF",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        352
      ],
      "parameters": {
        "jsCode": "const metrics = items[0].json;\nconst reportDate = metrics.reportDate;\n\nconst pdfContent = `Daily Report - ${reportDate}\\n\\nTotal Revenue: $${metrics.totalRevenue}\\nOpen Support Tickets: ${metrics.ticketsOpen}\\n`;\nconst buffer = Buffer.from(pdfContent, 'utf8');\n\nreturn [\n  {\n    json: {\n      ...metrics\n    },\n    binary: {\n      data: {\n        data: buffer.toString('base64'),\n        mimeType: 'application/pdf',\n        fileName: `daily_report_${reportDate}.pdf`\n      }\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "131ad3ae-3852-4ea6-8c3e-448f1b453b78",
      "name": "Upload Report to HubSpot",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        1392,
        272
      ],
      "parameters": {
        "resource": "file"
      },
      "typeVersion": 2
    },
    {
      "id": "ec51f979-224f-4a1c-9366-ccaa108bbaf2",
      "name": "HubSpot Upload OK?",
      "type": "n8n-nodes-base.if",
      "position": [
        1568,
        272
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.id || '' }}",
              "operation": "notEmpty"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "7253bd87-42f9-428f-86b5-570ff0151d47",
      "name": "Craft Mattermost Message",
      "type": "n8n-nodes-base.code",
      "position": [
        1760,
        256
      ],
      "parameters": {
        "jsCode": "const metrics = $json;\nconst fileId  = $json.id || 'unknown';\n\nreturn [\n  {\n    json: {\n      mattermostText: `#### Daily Report (${metrics.reportDate})  \\n\u2022 Total Revenue: $${metrics.totalRevenue}  \\n\u2022 Open Tickets: ${metrics.ticketsOpen}  \\n\u2022 HubSpot File ID: ${fileId}`,\n      channel: $json.mattermostChannel || 'DAILY_REPORTS'\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "47d5b37e-9ded-4c14-b3c0-fe4b1b5e0a1f",
      "name": "Send Mattermost Notification",
      "type": "n8n-nodes-base.mattermost",
      "position": [
        1968,
        256
      ],
      "parameters": {
        "operation": "create"
      },
      "typeVersion": 1
    },
    {
      "id": "ac1fb919-4804-4a14-a45f-46f2d9755968",
      "name": "Compose Error Message",
      "type": "n8n-nodes-base.code",
      "position": [
        1104,
        816
      ],
      "parameters": {
        "jsCode": "return [\n  {\n    json: {\n      mattermostText: `\u274c Daily Report workflow failed in one of the data or storage steps. Please review the execution logs.`,\n      channel: 'DAILY_REPORTS'\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "417ce5b0-f0b1-43a0-9424-a67a2c714282",
      "name": "Send Mattermost Error",
      "type": "n8n-nodes-base.mattermost",
      "position": [
        1408,
        816
      ],
      "parameters": {
        "operation": "create"
      },
      "typeVersion": 1
    },
    {
      "id": "46b38355-a115-4de5-b88b-f40d547d07e8",
      "name": "Respond to Caller",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2160,
        352
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "87976de2-b33a-4b77-afa6-3ee02696032c",
  "connections": {
    "Generate PDF": {
      "main": [
        [
          {
            "node": "Upload Report to HubSpot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Config": {
      "main": [
        [
          {
            "node": "Fetch Sales Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Support Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sales Data OK?": {
      "main": [
        [
          {
            "node": "Combine Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Compose Error Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Results": {
      "main": [
        [
          {
            "node": "Aggregate Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Prepare Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Sales Data": {
      "main": [
        [
          {
            "node": "Sales Data OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Support Data OK?": {
      "main": [
        [
          {
            "node": "Combine Results",
            "type": "main",
            "index": 1
          }
        ],
        [
          {
            "node": "Compose Error Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Metrics": {
      "main": [
        [
          {
            "node": "Generate PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Support Data": {
      "main": [
        [
          {
            "node": "Support Data OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HubSpot Upload OK?": {
      "main": [
        [
          {
            "node": "Craft Mattermost Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Compose Error Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compose Error Message": {
      "main": [
        [
          {
            "node": "Send Mattermost Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Mattermost Error": {
      "main": [
        [
          {
            "node": "Respond to Caller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Craft Mattermost Message": {
      "main": [
        [
          {
            "node": "Send Mattermost Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Report to HubSpot": {
      "main": [
        [
          {
            "node": "HubSpot Upload OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Mattermost Notification": {
      "main": [
        [
          {
            "node": "Respond to Caller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}