Workflow · Entrepreneurs

N8N Workflow for Startup Investor Update Reporting 2026

Auto-generate monthly investor reports by pulling metrics from Airtable, calculating KPIs, and sending formatted updates via email to your investor list.

This workflow automatically generates monthly investor update reports by pulling key metrics from your Airtable base, calculating growth percentages, and emailing formatted reports to your investor distribution list. It eliminates the 4-hour monthly process of manually gathering data, building charts, and crafting individual emails.

Why this automation matters

Most founders spend an entire afternoon each month pulling revenue data, user metrics, and burn rate numbers from different sources, then manually calculating month-over-month growth rates and crafting personalized investor emails. Without automation, it’s easy to miss monthly deadlines or send inconsistent data formats. This workflow ensures your investor updates go out on the same day each month with consistent formatting and accurate calculations.

What you need before starting

How to build it: step by step

1. Schedule Trigger — Monthly execution

Node type: Schedule Trigger Trigger Rule: Cron Expression Cron Expression: 0 9 1 * * Timezone: Your local timezone Output: Executes on the 1st day of each month at 9:00 AM Why this matters: Ensures consistent timing for investor updates and gives you the first day of the month to review any last-minute data before sending.

2. Airtable — Fetch latest metrics

Node type: Airtable Operation: List Base ID: Your startup metrics base ID Table: Monthly_Metrics Sort: Month (descending) Max Records: 2 Output: Returns the current month and previous month’s metrics as separate items Why this matters: Getting two months allows you to calculate growth percentages automatically rather than doing math manually.

3. Code — Calculate growth metrics

Node type: Code Mode: Run Once for All Items JavaScript Code:

const current = $input.all()[0];
const previous = $input.all()[1];

const revenueGrowth = ((current.json.Revenue - previous.json.Revenue) / previous.json.Revenue * 100).toFixed(1);
const userGrowth = ((current.json.Users - previous.json.Users) / previous.json.Users * 100).toFixed(1);
const burnRate = current.json.Burn_Rate;

return [{
  json: {
    currentMonth: current.json.Month,
    revenue: current.json.Revenue,
    revenueGrowth: revenueGrowth,
    users: current.json.Users,
    userGrowth: userGrowth,
    burnRate: burnRate,
    arr: current.json.ARR
  }
}];

Output: Single item with formatted metrics and calculated growth percentages Why this matters: Pre-calculating percentages prevents errors in your email template and ensures consistent number formatting.

4. Airtable — Get investor list

Node type: Airtable Operation: List Base ID: Your startup metrics base ID Table: Investors Filter Formula: Status = ‘Active’ Output: All active investors as individual items for the email loop Why this matters: Filtering by status prevents sending updates to investors who have requested to be removed or are no longer relevant.

5. Gmail — Send investor update

Node type: Gmail Operation: Send Email To: {{$json.Email}} Subject: {{$(‘Code’).item.json.currentMonth}} Investor Update - Revenue up {{$(‘Code’).item.json.revenueGrowth}}% Email Type: HTML HTML Body:

<p>Hi {{$json.Name}},</p>

<p>Here's our progress for {{$('Code').item.json.currentMonth}}:</p>

<h3>Key Metrics</h3>
<ul>
<li><strong>Revenue:</strong> ${{$('Code').item.json.revenue}} ({{$('Code').item.json.revenueGrowth}}% growth)</li>
<li><strong>Users:</strong> {{$('Code').item.json.users}} ({{$('Code').item.json.userGrowth}}% growth)</li>
<li><strong>ARR:</strong> ${{$('Code').item.json.arr}}</li>
<li><strong>Monthly Burn:</strong> ${{$('Code').item.json.burnRate}}</li>
</ul>

<p>Thanks for your continued support.</p>

<p>Best,<br>
[Your Name]</p>

Output: Individual emails sent to each active investor with personalized data Why this matters: Using the investor’s name and dynamic metrics creates personalized updates without manual customization for each recipient.

Full workflow JSON

{
  "name": "Startup Investor Update Automation",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "cronExpression": "0 9 1 * *"
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "Monthly Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [240, 300]
    },
    {
      "parameters": {
        "operation": "list",
        "base": {
          "__rl": true,
          "value": "// Replace with your Airtable base ID",
          "mode": "id"
        },
        "table": {
          "__rl": true,
          "value": "Monthly_Metrics",
          "mode": "string"
        },
        "sort": {
          "sortFields": [
            {
              "field": "Month",
              "direction": "desc"
            }
          ]
        },
        "limit": 2
      },
      "id": "airtable-metrics",
      "name": "Fetch Metrics",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 1,
      "position": [460, 300],
      "credentials": {
        "airtableOAuth2Api": {
          "id": "// Replace with your Airtable credential ID",
          "name": "Airtable OAuth2"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const current = $input.all()[0];\nconst previous = $input.all()[1];\n\nconst revenueGrowth = ((current.json.fields.Revenue - previous.json.fields.Revenue) / previous.json.fields.Revenue * 100).toFixed(1);\nconst userGrowth = ((current.json.fields.Users - previous.json.fields.Users) / previous.json.fields.Users * 100).toFixed(1);\nconst burnRate = current.json.fields.Burn_Rate;\n\nreturn [{\n  json: {\n    currentMonth: current.json.fields.Month,\n    revenue: current.json.fields.Revenue,\n    revenueGrowth: revenueGrowth,\n    users: current.json.fields.Users,\n    userGrowth: userGrowth,\n    burnRate: burnRate,\n    arr: current.json.fields.ARR\n  }\n}];"
      },
      "id": "code-calculate",
      "name": "Calculate Growth",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [680, 300]
    },
    {
      "parameters": {
        "operation": "list",
        "base": {
          "__rl": true,
          "value": "// Replace with your Airtable base ID",
          "mode": "id"
        },
        "table": {
          "__rl": true,
          "value": "Investors",
          "mode": "string"
        },
        "filterByFormula": "Status = 'Active'"
      },
      "id": "airtable-investors",
      "name": "Get Investors",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 1,
      "position": [900, 300],
      "credentials": {
        "airtableOAuth2Api": {
          "id": "// Replace with your Airtable credential ID",
          "name": "Airtable OAuth2"
        }
      }
    },
    {
      "parameters": {
        "operation": "send",
        "to": "={{$json.fields.Email}}",
        "subject": "={{$node['Calculate Growth'].json.currentMonth}} Investor Update - Revenue up {{$node['Calculate Growth'].json.revenueGrowth}}%",
        "emailType": "html",
        "html": "<p>Hi {{$json.fields.Name}},</p>\n\n<p>Here's our progress for {{$node['Calculate Growth'].json.currentMonth}}:</p>\n\n<h3>Key Metrics</h3>\n<ul>\n<li><strong>Revenue:</strong> ${{$node['Calculate Growth'].json.revenue}} ({{$node['Calculate Growth'].json.revenueGrowth}}% growth)</li>\n<li><strong>Users:</strong> {{$node['Calculate Growth'].json.users}} ({{$node['Calculate Growth'].json.userGrowth}}% growth)</li>\n<li><strong>ARR:</strong> ${{$node['Calculate Growth'].json.arr}}</li>\n<li><strong>Monthly Burn:</strong> ${{$node['Calculate Growth'].json.burnRate}}</li>\n</ul>\n\n<p>Thanks for your continued support.</p>\n\n<p>Best,<br>\n[Your Name]</p>"
      },
      "id": "gmail-send",
      "name": "Send Update Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 1,
      "position": [1120, 300],
      "credentials": {
        "gmailOAuth2": {
          "id": "// Replace with your Gmail credential ID",
          "name": "Gmail OAuth2"
        }
      }
    }
  ],
  "connections": {
    "Monthly Trigger": {
      "main": [
        [
          {
            "node": "Fetch Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Metrics": {
      "main": [
        [
          {
            "node": "Calculate Growth",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Growth": {
      "main": [
        [
          {
            "node": "Get Investors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Investors": {
      "main": [
        [
          {
            "node": "Send Update Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Frequently Asked Questions

What happens if I don’t have data for the previous month when calculating growth?

The Code node will throw a division error and the workflow will fail. Add a manual entry to your Monthly_