Automate Monthly Revenue Report → Stripe + HubSpot + Slack + Sheets in n8n — Step by Step

Every month, someone on your team burns half a day pulling numbers out of Stripe, cross-referencing them against HubSpot deals, pasting it all into a spreadsheet, and dropping a summary into Slack for

Automate Monthly Revenue Report → Stripe + HubSpot + Slack + Sheets in n8n — Step by Step

Every month, someone on your team burns half a day pulling numbers out of Stripe, cross-referencing them against HubSpot deals, pasting it all into a spreadsheet, and dropping a summary into Slack for the leadership channel. It's manual, it's error-prone, and it's the kind of work that quietly eats the first three days of every month. Worse: the report is stale the moment it's built, and nobody trusts the numbers because the process changes every time a different person runs it.

This article walks through a production-ready n8n workflow that generates your monthly revenue report automatically — pulling MRR and payment data from Stripe, deal and pipeline data from HubSpot, writing a clean row to Google Sheets, and posting a formatted summary to Slack — on the 1st of every month, with zero human intervention.

The problem: revenue lives in four places and none of them talk

Your actual revenue picture is fragmented across systems that were never designed to reconcile with each other:

  • Stripe knows what money actually moved — charges, refunds, subscription renewals, MRR.
  • HubSpot knows the commercial context — which deals closed, deal size, owner, pipeline stage.
  • Google Sheets is where finance and leadership expect the historical record to live.
  • Slack is where the team actually reads the summary.

The manual process of stitching these together isn't just slow — it introduces judgment calls. Did last month's refunds get netted out? Did that annual deal get counted as $12k this month or amortized? When a human does it by hand, the methodology drifts, and month-over-month comparisons become meaningless. The fix isn't discipline. It's automation that enforces one deterministic method every single time.

The solution: one scheduled n8n workflow, four connected nodes

n8n is a fair-code workflow automation tool that connects APIs without you writing glue code or maintaining a server full of cron scripts. The workflow here follows a simple linear-then-merge shape:

  1. A Schedule Trigger fires on the 1st of each month.
  2. A Stripe node pulls the previous month's charges and subscription revenue.
  3. A HubSpot node pulls deals closed-won in the same window.
  4. A Code / Set node computes the totals and assembles the report object.
  5. A Google Sheets node appends a new row to your historical ledger.
  6. A Slack node posts the formatted summary to your revenue channel.

Because everything runs inside one workflow, the methodology is codified once. Every report is calculated identically, and the audit trail lives in n8n's execution log.

Step-by-step setup in n8n

1. The Schedule Trigger

Add a Schedule Trigger node. Set the trigger interval to Cron and use the expression 0 6 1 * * — that fires at 06:00 on day 1 of every month. Running at 6am means the report is waiting in Slack before anyone opens their laptop. Set your timezone explicitly in the workflow settings so the month boundary doesn't shift on you.

2. Pull revenue from Stripe

Add a Stripe node authenticated with your restricted API key (read-only on Charges and Subscriptions is enough — never use a full-access key here). Use the Charge → Get Many operation, and filter by a created range covering the previous calendar month. Compute the boundaries dynamically with an expression:

created[gte] = {{ DateTime.now().minus({months:1}).startOf('month').toSeconds() }}
created[lt]  = {{ DateTime.now().startOf('month').toSeconds() }}

Enable Return All so pagination is handled automatically — otherwise you silently cap at 100 charges and undercount a busy month. If you bill on subscriptions, add a second Stripe operation to pull Invoice → Get Many with status=paid for a cleaner MRR figure than raw charges give you.

3. Pull closed deals from HubSpot

Add a HubSpot node using the Deal → Get Many (search) operation. Filter on dealstage = closedwon and closedate within the same previous-month window. Request the amount, dealname, hs_object_id, and hubspot_owner_id properties so you can break revenue down by rep later if you want. Use HubSpot's search endpoint (not the list endpoint) so the date filtering happens server-side and you don't drag your entire deal history across the wire.

4. Aggregate the numbers

Add a Code node (JavaScript) that receives both branches and computes the summary. Sum Stripe charge amount values (remember Stripe returns cents — divide by 100), subtract refunds, and sum HubSpot deal amounts. Build one clean output object:

const stripeTotal = $('Stripe').all()
  .reduce((s, i) => s + i.json.amount, 0) / 100;
const hubspotTotal = $('HubSpot').all()
  .reduce((s, i) => s + Number(i.json.amount || 0), 0);

return [{ json: {
  month: DateTime.now().minus({months:1}).toFormat('MMMM yyyy'),
  stripe_revenue: stripeTotal,
  hubspot_closed: hubspotTotal,
  deals_count: $('HubSpot').all().length,
  generated_at: DateTime.now().toISO()
}}];

To merge the two independent branches cleanly, place a Merge node (mode: Combine → All) before the Code node, or simply reference each node by name with the $('NodeName') syntax as shown above.

5. Append to Google Sheets

Add a Google Sheets node with the Append Row operation. Point it at your "Revenue Ledger" spreadsheet and map the fields from the Code node: month, stripe_revenue, hubspot_closed, deals_count, generated_at. Appending (not updating) means you accumulate a permanent month-over-month history you can chart directly inside Sheets.

6. Post the summary to Slack

Finish with a Slack node using Message → Post. Target your #revenue channel and build a formatted message with Slack's Blocks so it reads like a real report, not a JSON dump:

📊 *Revenue Report — {{ $json.month }}*
• Stripe collected: *${{ $json.stripe_revenue.toLocaleString() }}*
• HubSpot closed-won: *${{ $json.hubspot_closed.toLocaleString() }}*
• Deals closed: {{ $json.deals_count }}
Full history → [Google Sheet link]

The payoff: what you actually get

Once this is live, the returns compound:

  • Three days back every month. The work that used to block the start of every month now happens while everyone sleeps.
  • One methodology, forever. Refunds, date windows, and deal-stage definitions are enforced in code, so month-over-month numbers are finally comparable.
  • A trustworthy historical ledger. Sheets accumulates a clean series you can pivot, chart, or feed into a board deck.
  • Instant distribution. The team reads it in Slack where they already work — no one has to go hunting for the report.
  • An audit trail. Every run is logged in n8n with the exact data pulled, so you can always answer "where did this number come from?"

Common pitfalls to avoid

Timezone drift at the month boundary. If your n8n instance runs UTC but your business reports in a different zone, "the previous month" can include or exclude a day of transactions. Pin the timezone in workflow settings and in your DateTime expressions.

Stripe amounts are in cents. Forgetting to divide by 100 produces a report showing 100× your real revenue. It's the single most common mistake — bake the division into the Code node and never touch it again.

Pagination limits. Both Stripe and HubSpot cap results per request. Always enable Return All on the Stripe node and use HubSpot's search with proper paging, or you'll quietly undercount high-volume months and never notice.

Double-counting revenue. Stripe collections and HubSpot closed-won deals overlap conceptually — a closed deal often becomes a Stripe charge. Decide upfront whether these are two independent views (cash vs. commercial) or whether you need to deduplicate. Label them distinctly in the report so no one adds the two numbers together by accident.

No failure alert. A silent workflow that fails on the 1st means no report and nobody knows. Add an Error Trigger workflow that posts to Slack if the run throws, so a broken API key surfaces immediately instead of on the 5th when someone asks where the report is.

Set this up once and monthly revenue reporting stops being a task. It becomes infrastructure — quiet, consistent, and running whether or not anyone remembers it exists.

Monthly Revenue Report → Stripe + HubSpot + Slack + Sheets
PRONTO PARA USAR

Ja construimos isso pra voce

Nao comece do zero. O Monthly Revenue Report → Stripe + HubSpot + Slack + Sheets e um workflow n8n pronto para instalar — conecta suas ferramentas em minutos, sem codigo.

Instalar por $119 →