Monthly revenue report by email

On the 1st of each month, get last month's Stripe revenue compared with the month before.

Medium Saves ~2 hours/month Finance & AdminBy Flowngn team

Works with n8n · Make · Zapier

How it works

Trigger: Schedule — 1st of every month at 8:00
  1. Get recent charges from Stripe.
  2. Add up successful charges for last month and the month before.
  3. Email yourself both totals and the growth percentage.

Diagram

1st of month 8:00Schedule TriggerSGet chargesStripe{ }Compare monthsCodeGEmail reportGmail
Trigger App Code

Code

JavaScript used in the Code step — already included in the template below.

Compare months · Code node
// Compares last month's successful charges with the month before.
const now = new Date();
const startOf = (monthsAgo) => new Date(now.getFullYear(), now.getMonth() - monthsAgo, 1).getTime() / 1000;

const lastStart = startOf(1);
const thisStart = startOf(0);
const prevStart = startOf(2);

const charges = $input.all().map((item) => item.json).filter((c) => c.status === "succeeded" && c.paid);
const total = (from, to) =>
  charges.filter((c) => c.created >= from && c.created < to).reduce((sum, c) => sum + c.amount / 100, 0);

const last = total(lastStart, thisStart);
const prev = total(prevStart, lastStart);
const growth = prev ? Math.round(((last - prev) / prev) * 100) : 0;
const month = new Date(lastStart * 1000).toLocaleString("en-US", { month: "long", year: "numeric" });

return [{
  json: {
    subject: `Revenue report — ${month}`,
    body:
      `${month}: ${last.toFixed(2)}\n` +
      `Month before: ${prev.toFixed(2)}\n` +
      `Growth: ${growth >= 0 ? "▲" : "▼"} ${Math.abs(growth)}%`,
  },
}];

Build it

Pick your tool — same automation, step by step.

Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder.

Workflow JSON
Show code (131 lines)
{
  "name": "Monthly revenue report by email (Flowngn)",
  "nodes": [
    {
      "parameters": {
        "content": "## Monthly revenue report by email\nOn the 1st of each month, get last month's Stripe revenue compared with the month before.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
        "height": 260,
        "width": 340
      },
      "id": "2f780998-9838-445d-a923-1c16b94b02ed",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -420,
        -60
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months",
              "triggerAtDayOfMonth": 1,
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "2f780998-9838-445d-a923-1c16b94b02ed",
      "name": "1st of month 8:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "resource": "charge",
        "operation": "getAll",
        "returnAll": true
      },
      "id": "2f780998-9838-445d-a923-1c16b94b02ed",
      "name": "Get charges",
      "type": "n8n-nodes-base.stripe",
      "typeVersion": 1,
      "position": [
        300,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Compares last month's successful charges with the month before.\nconst now = new Date();\nconst startOf = (monthsAgo) => new Date(now.getFullYear(), now.getMonth() - monthsAgo, 1).getTime() / 1000;\n\nconst lastStart = startOf(1);\nconst thisStart = startOf(0);\nconst prevStart = startOf(2);\n\nconst charges = $input.all().map((item) => item.json).filter((c) => c.status === \"succeeded\" && c.paid);\nconst total = (from, to) =>\n  charges.filter((c) => c.created >= from && c.created < to).reduce((sum, c) => sum + c.amount / 100, 0);\n\nconst last = total(lastStart, thisStart);\nconst prev = total(prevStart, lastStart);\nconst growth = prev ? Math.round(((last - prev) / prev) * 100) : 0;\nconst month = new Date(lastStart * 1000).toLocaleString(\"en-US\", { month: \"long\", year: \"numeric\" });\n\nreturn [{\n  json: {\n    subject: `Revenue report — ${month}`,\n    body:\n      `${month}: ${last.toFixed(2)}\\n` +\n      `Month before: ${prev.toFixed(2)}\\n` +\n      `Growth: ${growth >= 0 ? \"▲\" : \"▼\"} ${Math.abs(growth)}%`,\n  },\n}];"
      },
      "id": "2f780998-9838-445d-a923-1c16b94b02ed",
      "name": "Compare months",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        600,
        0
      ]
    },
    {
      "parameters": {
        "sendTo": "you@yourcompany.com",
        "subject": "={{ $json.subject }}",
        "emailType": "text",
        "message": "={{ $json.body }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "2f780998-9838-445d-a923-1c16b94b02ed",
      "name": "Email report",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        900,
        0
      ]
    }
  ],
  "connections": {
    "1st of month 8:00": {
      "main": [
        [
          {
            "node": "Get charges",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get charges": {
      "main": [
        [
          {
            "node": "Compare months",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare months": {
      "main": [
        [
          {
            "node": "Email report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Open in the playground to edit →

Tips

  • For more than a few hundred payments a month, use Stripe's reporting exports instead.

Related flows