HubSpot Deal Stage + Gmail Thread Age: Daily Stale-Deal Escalation Digest to Slack via Make
Surface every deal that's gone dark — no reply in 5+ days, still open in HubSpot — every morning before your sales standup.
The stack in the order it runs — data flows from the source through to where it lands.
The single most common reason early-stage SaaS deals die isn't price or product fit — it's follow-up latency. A founder or AE moves a deal to 'Proposal Sent' in HubSpot and gets pulled into product work. Five days pass. The prospect's buying intent cools. By the time anyone checks, it's been two weeks and the deal is dead. HubSpot's built-in task reminders require someone to set them manually per deal. Nobody does.
Make (formerly Integromat) handles the Gmail API's OAuth flow and batch message retrieval cleanly through its built-in Gmail module — zero auth code written by you. The multi-router pattern lets you split logic: one route for deals in 'Proposal Sent' with no Gmail thread activity in 5+ days, another for 'Negotiation' deals with no activity in 3+ days. Zapier can't do multi-path logic in one Zap without Multi-Step premium. n8n can do it but costs you more setup time on Gmail OAuth.
The real tradeoff: Gmail thread matching to HubSpot deals only works if your team's email is logged in HubSpot via HubSpot's Gmail extension or BCC logging. If it's not, fall back to HubSpot's own `hs_last_activity_date` property — less granular, but still usable. The Slack output is one daily digest message, not one message per deal. It lists deals as bullet points with owner name, deal name, days since last contact, and a direct HubSpot deal link. That's intentional — a per-deal Slack flood gets muted inside a week.
Skip this if you're running Outreach or Salesloft — those have native sequence lapse alerts built in. This is for teams running sales entirely out of HubSpot and Gmail with no dedicated sales ops infrastructure.
The stack (4)
How it runs
- 1
Create a Make scenario with a daily schedule trigger
In Make, create a new scenario. Add a 'Schedule' trigger set to run daily at 8 AM in your local timezone. Name it 'Stale Deal Escalation – Daily'. In scenario settings, increase the timeout to 300 seconds — HubSpot's search API and Gmail batch lookups are slow when you have more than 50 open deals. Enable 'Auto-commit' so partial runs still post whatever data was collected before a timeout fires.
- 2
Pull open deals from HubSpot filtered by deal stage
Add a HubSpot 'Search CRM Objects' module. Object type: Deals. Filter: `dealstage IN ['proposal_sent', 'negotiation', 'contract_sent']` AND `closedate >= TODAY` — that drops already-closed or past-due deals. Properties to retrieve: `dealname`, `dealstage`, `hubspot_owner_id`, `hs_last_activity_date`, `hs_email_last_reply_date`, `associations.contacts`. Set result limit to 100. If you have more than 100 open deals in these stages, add a pagination iterator using Make's built-in array aggregator after the search module.
- 3
Calculate days since last activity per deal
Add a 'Set Variable' module — or use Make's Tools > Set Multiple Variables. For each deal, compute `days_since_activity = DATEDIFF(NOW(), hs_last_activity_date, 'days')` and `days_since_email_reply = DATEDIFF(NOW(), hs_email_last_reply_date, 'days')`. Use Make's `formatDate` and `dateDifference` functions — they handle timezone normalization correctly. Output both values as integers so the filter step doesn't choke on string comparisons.
- 4
Filter to only genuinely stale deals
Add a Router module with two paths. Path A: `dealstage = 'proposal_sent'` AND `days_since_activity >= 5`. Path B: `dealstage IN ['negotiation', 'contract_sent']` AND `days_since_activity >= 3`. Both paths should also filter out deals where `hubspot_owner_id` is null — unowned deals need a separate triage process, not this digest. Each path feeds into its own array aggregator to collect matching deals before building the Slack message. This is what prevents one Slack notification per deal.
- 5
Look up owner name from HubSpot
For each deal in both paths, add a HubSpot 'Get an Owner' module using `hubspot_owner_id` from the deal. Retrieve `firstName`, `lastName`, and `email`. That's what lets you show 'Owned by Sarah Chen' instead of a raw user ID in the Slack message. If you're processing a lot of deals, cache owner lookups using Make's Data Store — HubSpot's owner API isn't heavily rate-limited, but repeated identical calls on the same owner IDs slow the scenario for no reason.
- 6
Build the Slack digest message from aggregated deal lists
After each array aggregator, add a 'Text Aggregator' module that formats each deal as a single line: `• *{{dealname}}* ({{dealstage}}) — {{days_since_activity}} days idle — Owner: {{ownerName}} — <{{deal_url}}|Open in HubSpot>`. The deal URL is `https://app.hubspot.com/contacts/YOUR_PORTAL_ID/deal/{{deal_id}}`. Combine Path A and Path B text outputs using a second aggregator or a Set Variable module that concatenates both strings with a section header between them.
- 7
Post the digest to Slack with conditional logic
Add a Slack 'Create a Message' module pointing at `#sales-standup`. Only send if the total stale deal count is greater than 0 — put a Filter module before the Slack step checking that the aggregated text is not empty. Message structure: header `🔴 *Stale Deal Alert – {{TODAY}}*`, then Path A deals under `*Proposals (5+ days idle):*`, then Path B deals under `*Active Negotiations (3+ days idle):*`. Use Slack's `mrkdwn` formatting for bold and links.
- 8
Post a clean confirmation when no deals are stale
Add a second Slack message on the else path — when stale count equals 0 — posting to `#sales-standup-log`, not the main sales channel: `✅ No stale deals as of {{TODAY}}. All open deals have activity within SLA.` This creates an audit trail that the workflow ran and found nothing. That matters because if the scenario silently errors, people assume 'no alert = no stale deals' when actually nothing ran. Set this message to reply to the previous day's log message using `thread_ts` if you want to keep the channel clean.
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
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.
Outlook Calendar Load + HubSpot Deal Velocity: Weekly Ops Digest to Microsoft Teams
Every Monday at 07:30, your revenue team gets one Teams card: last week's deal pipeline movement next to each rep's actual meeting load — so you stop guessing whether low close rates are a pipeline problem or a capacity problem.