AutomationFlowsWeb Scraping › Process Incoming Files and Notify via Email with Github Storage

Process Incoming Files and Notify via Email with Github Storage

Byvinci-king-01 @vinci-king-01 on n8n.io

This workflow automatically ingests newly-uploaded files, validates and transforms their contents, stores the processed files in a GitHub repository, and sends email notifications upon completion. It is ideal for teams that regularly receive data drops and need an auditable,…

Cron / scheduled trigger★★★★☆ complexity21 nodesHTTP RequestGitHubEmail Send
Web Scraping Trigger: Cron / scheduled Nodes: 21 Complexity: ★★★★☆ Added:

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

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

The workflow JSON

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

Download .json
{
  "id": "JZ8C3IFRtHrntshw",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "File Processing Pipeline with Email and GitHub",
  "tags": [],
  "nodes": [
    {
      "id": "ef26b9a6-1831-46e6-bcab-181075c3a072",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -384,
        208
      ],
      "parameters": {
        "width": 550,
        "height": 706,
        "content": "## How it works\nThis workflow runs on a fixed schedule and checks an external endpoint for any newly-uploaded files that need processing.  If files exist, each one is downloaded in turn and its type is inspected.  CSV files are parsed inside a Code node and the resulting records are validated against simple business rules.  Valid data are committed to a GitHub repository, while any problems (missing fields, wrong file type, or empty job queue) trigger an error-handling branch.  After every run the appropriate success or failure email is issued so stakeholders always know the outcome.\n\n## Setup steps\n1. Create an HTTP endpoint that returns a JSON array of pending files (id, url, fileName, mimeType).\n2. Add GitHub OAuth credentials in n8n and replace the owner/repo placeholders.\n3. Configure an SMTP credential for the Email Send node and update the to/from addresses.\n4. Adjust the schedule trigger interval to suit your cadence.\n5. Edit validation rules inside the **Validate Data** Code node as needed.\n6. (Optional) Tweak commit paths, branch names or email templates for your environment."
      },
      "typeVersion": 1
    },
    {
      "id": "7b77f257-297a-4c42-b211-f3923de8c071",
      "name": "Trigger & Fetch",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        496,
        304
      ],
      "parameters": {
        "color": 7,
        "width": 994,
        "height": 654,
        "content": "## Trigger & Retrieval\nThis section contains the **Schedule Trigger** that launches the workflow and the HTTP Request that fetches the list of files waiting to be processed.  A preliminary Code node counts the files so the following IF node can decide whether to continue or exit early.  If no files are returned we immediately branch to the notification path so stakeholders know the run had nothing to do.  Keeping retrieval logic isolated here makes it easier to swap in an S3 or SharePoint source later without touching the downstream processing chain."
      },
      "typeVersion": 1
    },
    {
      "id": "e57d8c88-feed-49e8-85d1-6d7a74cc246c",
      "name": "Processing & Validation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1520,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 1026,
        "height": 654,
        "content": "## Processing & Validation\nNodes in this cluster handle per-file work.  Files arrive one at a time via **SplitInBatches**, get downloaded, then routed by an IF node that checks MIME/type.  CSVs are parsed in **Parse CSV** and examined in **Validate Data** for required columns, data types and any custom business rules you add.  Failures are piped straight to the error-email branch, while clean data move forward to storage.  This modular approach lets you bolt on extra transforms (enrichment, filtering, aggregation) without breaking existing logic."
      },
      "typeVersion": 1
    },
    {
      "id": "f1c7b100-a7bc-47f2-8bfa-43c5ec84d1f6",
      "name": "Storage & Notification",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2576,
        304
      ],
      "parameters": {
        "color": 7,
        "width": 994,
        "height": 622,
        "content": "## Storage & Notification\nAfter a record set passes validation, it is wrapped in JSON, committed to GitHub and the success branch crafts a confirmation email.  Any failure along the way jumps to an alternate Set+Email pair so the operations team receives actionable information.  Because commit metadata, email subject and body are assembled in dedicated **Set** nodes, you can change wording, recipients or file-naming conventions in one place without touching upstream logic."
      },
      "typeVersion": 1
    },
    {
      "id": "267a08d7-e5dd-4beb-8e7e-53f71655aab2",
      "name": "Daily File Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        512,
        576
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "45b00107-991d-410a-a9c1-b1d454ac6d38",
      "name": "Fetch File List",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        704,
        576
      ],
      "parameters": {
        "url": "https://api.example.com/uploads/pending",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "acf76dcf-9922-4e18-a640-d18b8143eb61",
      "name": "Check File Count",
      "type": "n8n-nodes-base.code",
      "position": [
        912,
        576
      ],
      "parameters": {
        "jsCode": "// Expect an object like { files: [...] }\nconst files = $json.files || [];\nreturn [{ json: { files, fileCount: files.length } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "f55e226c-09e7-40df-a0d2-032e4b8325bd",
      "name": "Any New Files?",
      "type": "n8n-nodes-base.if",
      "position": [
        1104,
        576
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "number": [
            {
              "value1": "={{ $json.fileCount }}",
              "value2": 0,
              "operation": "larger"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "5e90a2c9-8a61-4158-9d0e-ccbbaf3671a8",
      "name": "Prepare File Items",
      "type": "n8n-nodes-base.code",
      "position": [
        1312,
        576
      ],
      "parameters": {
        "jsCode": "// Expand array into individual items\nreturn ($json.files || []).map(f => ({ json: f }));"
      },
      "typeVersion": 2
    },
    {
      "id": "eab73259-410f-40ae-add9-d0ab535dc08b",
      "name": "Iterate Files",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1504,
        576
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "3a5a7674-e38e-4325-8d49-889ec1f43ad5",
      "name": "Download File",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1712,
        576
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "2c1cc809-e708-4a24-b792-ef052ac88897",
      "name": "Is CSV?",
      "type": "n8n-nodes-base.if",
      "position": [
        1904,
        576
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.mimeType || $json.type || 'unknown' }}",
              "value2": "text/csv",
              "operation": "equals"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2b67be02-6092-4309-8f8f-2106caeb58fe",
      "name": "Parse CSV",
      "type": "n8n-nodes-base.code",
      "position": [
        2112,
        576
      ],
      "parameters": {
        "jsCode": "// Simple CSV parser (no external deps)\nconst csvText = $json.body;\nconst lines = csvText.trim().split(/\\r?\\n/);\nconst headers = lines.shift().split(',');\nconst records = lines.map(line => {\n  const cols = line.split(',');\n  const obj = {};\n  headers.forEach((h, i) => { obj[h.trim()] = (cols[i] || '').trim(); });\n  return obj;\n});\nreturn [{ json: { originalFileName: $json.fileName, records } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "e775790b-55c1-4835-aab4-2ccef039a327",
      "name": "Validate Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2304,
        576
      ],
      "parameters": {
        "jsCode": "const items = $input.item.records;\nconst invalid = items.filter(r => !r.id || !r.id.length);\nreturn [{ json: {\n  originalFileName: $input.item.originalFileName,\n  data: items,\n  isValid: invalid.length === 0,\n  errors: invalid\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "bdfd326d-0806-44b8-bab3-5cc1b3a29426",
      "name": "Validation Passed?",
      "type": "n8n-nodes-base.if",
      "position": [
        2656,
        576
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.isValid }}",
              "operation": "isTrue"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "77243b3b-8b6f-403b-abac-1900ca662af1",
      "name": "Prepare GitHub Commit",
      "type": "n8n-nodes-base.set",
      "position": [
        2832,
        496
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "9595ba2f-a213-4bc6-9813-8b3e83bbb226",
      "name": "Create/Update File",
      "type": "n8n-nodes-base.github",
      "position": [
        2992,
        480
      ],
      "parameters": {
        "owner": "{{YOUR_GITHUB_USERNAME}}",
        "labels": [],
        "assignees": [],
        "repository": "{{YOUR_REPOSITORY_NAME}}"
      },
      "typeVersion": 1
    },
    {
      "id": "d7468b8b-c647-411a-9a6f-8f65ebff11d8",
      "name": "Success Email Content",
      "type": "n8n-nodes-base.set",
      "position": [
        3152,
        512
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "814955dc-d483-470d-af17-a8feb058678c",
      "name": "Send Success Email",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        3360,
        544
      ],
      "parameters": {
        "options": {},
        "subject": "={{ $json.subject }}",
        "toEmail": "={{ $json.toEmail }}",
        "fromEmail": "={{ $json.fromEmail }}"
      },
      "typeVersion": 2
    },
    {
      "id": "94405ec6-a93c-412c-8a2b-f1973ca4b650",
      "name": "Error Email Content",
      "type": "n8n-nodes-base.set",
      "position": [
        2768,
        784
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "cc483eaf-cce5-4232-8769-269f296f03d1",
      "name": "Send Error Email",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        3040,
        784
      ],
      "parameters": {
        "options": {},
        "subject": "={{ $json.subject }}",
        "toEmail": "={{ $json.toEmail }}",
        "fromEmail": "={{ $json.fromEmail }}"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "7e62b283-e4ce-4942-9ba2-1f78c04280ae",
  "connections": {
    "Is CSV?": {
      "main": [
        [
          {
            "node": "Parse CSV",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse CSV": {
      "main": [
        [
          {
            "node": "Validate Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download File": {
      "main": [
        [
          {
            "node": "Is CSV?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Iterate Files": {
      "main": [
        [
          {
            "node": "Download File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Data": {
      "main": [
        [
          {
            "node": "Validation Passed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Any New Files?": {
      "main": [
        [
          {
            "node": "Prepare File Items",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch File List": {
      "main": [
        [
          {
            "node": "Check File Count",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check File Count": {
      "main": [
        [
          {
            "node": "Any New Files?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily File Check": {
      "main": [
        [
          {
            "node": "Fetch File List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create/Update File": {
      "main": [
        [
          {
            "node": "Success Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare File Items": {
      "main": [
        [
          {
            "node": "Iterate Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validation Passed?": {
      "main": [
        [
          {
            "node": "Prepare GitHub Commit",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Email Content": {
      "main": [
        [
          {
            "node": "Send Error Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare GitHub Commit": {
      "main": [
        [
          {
            "node": "Create/Update File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Success Email Content": {
      "main": [
        [
          {
            "node": "Send Success Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

This workflow automatically ingests newly-uploaded files, validates and transforms their contents, stores the processed files in a GitHub repository, and sends email notifications upon completion. It is ideal for teams that regularly receive data drops and need an auditable,…

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

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

This workflow is an improvement of this workflow by Greg Brzezinka.

HTTP Request, Email Send, XML +1
Web Scraping

N8N-Workflow-Github-Manager. Uses github, httpRequest, n8n. Scheduled trigger; 38 nodes.

GitHub, HTTP Request, n8n
Web Scraping

N8N-Self-Updater. Uses ssh, emailSend, httpRequest. Scheduled trigger; 27 nodes.

Ssh, Email Send, HTTP Request
Web Scraping

> An automated n8n workflow originally built for DigitalOcean-based n8n deployments, but fully compatible with any VPS or cloud hosting (e.g., AWS, Google Cloud, Hetzner, Linode, etc.) where n8n ru

Ssh, Email Send, HTTP Request
Web Scraping

What if you could spot a major sales problem—or a winning campaign—the very next morning, instead of weeks later? Imagine receiving a beautiful, data-rich alert directly in your inbox the moment your

QuickBooks, HTTP Request, Email Send