HubSpot Deal Slip + Stripe MRR Gap Reconciliation: Weekly Risk Digest to Slack via Make
Surface the gap between what HubSpot says closed and what Stripe actually collected — before it blows up at quarter-end.
The stack in the order it runs — data flows from the source through to where it lands.
The most common ops failure in early-stage B2B SaaS: deals marked Closed Won in HubSpot that never converted to active Stripe subscriptions, and deals marked Closed Lost where Stripe still shows an active subscription — usually a payment that went through before someone updated the deal. This playbook runs every Monday, cross-references both systems, and flags every mismatch before it becomes a finance argument.
Make (formerly Integromat) is the right glue here because it has native HubSpot and Stripe modules that handle OAuth and pagination without you writing a single API loop. The visual scenario editor also makes the cross-reference logic auditable — and when a workflow touches revenue data people will argue about, auditability matters.
The join key between the two systems is email. HubSpot's deal API returns `dealstage`, `closedate`, and `associations.contacts`. You pull the associated contact's email, then hit Stripe's customer search API (`/v1/customers/search?query=email:'x'`) to retrieve active subscriptions. If your HubSpot contact records have dirty email data, you'll get false mismatches — fix that first.
Skip this if you're PLG. If users self-serve without a sales rep ever creating a HubSpot deal, you'll have thousands of Stripe customers with no corresponding deal and this workflow generates noise. This is specifically for sales-assisted or hybrid motions where every Closed Won deal should map to a Stripe subscription.
The stack (4)
How it runs
- 1
Create a Make scenario with a weekly schedule
In Make, create a new scenario. Add a Schedule trigger set to run every Monday at 7 AM — set the timezone to your business timezone. Name it `HubSpot-Stripe MRR Reconciliation`. Store your HubSpot Private App token and Stripe Secret Key as Make connections, using Make's built-in HubSpot and Stripe connection types so OAuth is handled without custom headers.
- 2
Fetch HubSpot deals closed in the past 30 days
Add a HubSpot Search for Deals module. Filter by `dealstage = closedwon` and `closedate` within the last 30 days. Request properties: `dealname`, `amount`, `closedate`, `hs_object_id`. Then add a HubSpot Get Associated Contacts module — associate each deal with its contacts and pull the contact's `email` property. Email is the join key to Stripe. If a deal has no associated contact with an email, flag it as a data hygiene issue — not a revenue mismatch. Keep those two categories separate.
- 3
Look up each contact email in Stripe
Add a Stripe Make an API Call module set to GET `/v1/customers/search?query=email:'{{email}}'&expand[]=data.subscriptions`. This returns the Stripe customer object with active subscriptions inline. For each Closed Won deal you're answering two questions: does a Stripe customer with this email exist, and do they have an active subscription? Capture `customer.id`, `subscriptions.data[0].status`, and `subscriptions.data[0].plan.amount` for the comparison.
- 4
Run the same check for Closed Lost deals
Add a second HubSpot Search for Deals module, this time filtering for `dealstage = closedlost` and `closedate` within the last 30 days. Run the same Stripe customer lookup. You're looking for the inverse: Closed Lost deals where the Stripe customer has an `active` subscription. That means either the deal stage was updated wrong or a payment went through after the deal was lost. This is where billing ops mistakes hide most often.
- 5
Aggregate mismatches in a Make Array Aggregator
Add a Make Array Aggregator module that collects two arrays: `closed_won_no_stripe` (Closed Won deals with no active Stripe subscription) and `closed_lost_active_stripe` (Closed Lost deals with an active Stripe subscription). Each item should include: deal name, deal amount, close date, contact email, and Stripe subscription status. Also compute `total_mrr_at_risk` by summing the `amount` field across all `closed_won_no_stripe` deals.
- 6
Format and post the digest to Slack
Add a Slack Create a Message module posting to `#revenue-ops`. Build the message in three sections: (1) a summary line — 'MRR Reconciliation: {N} Closed Won deals have no active Stripe sub | {M} Closed Lost deals still active in Stripe | $X MRR at risk'; (2) a bullet list of `closed_won_no_stripe` deals with deal name, amount, and email; (3) a bullet list of `closed_lost_active_stripe` deals. If both arrays are empty, post a green checkmark summary: 'All Closed Won deals have active Stripe subscriptions. No anomalies found.' That clean-run message is your confirmation the automation is actually working.
- 7
Create Airtable records for each mismatch requiring action
After the Slack message, add an Airtable Create a Record module for each item in both mismatch arrays. Table: `CRM-Billing Reconciliation`. Fields: Deal Name, Type (Closed Won No Sub / Closed Lost Active Sub), Amount, Contact Email, Close Date, Status (default: Open), Assigned To (leave blank for manual assignment). This gives revenue ops or sales a lightweight action queue to work through during the week — items get marked Resolved as they're fixed.
- 8
Add an error handler for API failures
In Make, add an error handler route on the Stripe lookup module. If Stripe returns a 429 (rate limit) or 5xx, route to a Slack Create a Message module that posts to `#ops-alerts`: 'MRR Reconciliation scenario failed at Stripe lookup — check Make execution log.' Without this, a Stripe API blip silently skips the entire reconciliation run and you won't know for a week.
Want me to build this for you instead?
Product Audit and CTO Mode run out of this same thinking. If you’re reading this thinking “I want this, but in my product” — let’s talk.
See servicesMore like this
Google Analytics 4 + HubSpot Lifecycle Stage: Weekly Acquisition-Quality Digest to Slack via Pipedream with Claude Narrative
Stop reporting traffic numbers. Report whether the traffic you paid for last week actually became pipeline — with a one-paragraph executive summary written by Claude.
Gmail Thread Aging + Stripe Invoice Overdue: Unified AR Follow-Up Digest to Slack via Zapier
Surface overdue Stripe invoices and the exact age of your last Gmail thread with that customer — every morning at 8:30, automatically — so AR follow-up stops living in someone's head.
Intercom Ticket Volume + Razorpay Failed Payments: Daily Support-Cost-per-Revenue Alert to Slack via Zapier
Catch the support cost blowout from Razorpay failed payments before your agents are already buried.