Daily Snowflake churn risk scores on every HubSpot contact
Each morning, score your active customers for churn risk from warehouse usage data and write the tier, score, and reason onto their HubSpot contact.
Every day at 8am in my timezone, score every active customer for churn risk from product usage in Snowflake and write the result back onto their contact in HubSpot, then post a Slack summary of the worst offenders to my CS channel. Trigger is a daily cron.
Step 1. Pull usage signals from Snowflake. Use Execute SQL Statement on Snowflake to run a single query that returns, for every active customer account, the following columns: account_id, primary contact email, account owner email, MRR or plan tier, logins in the last 14 days, logins in the last 30 days, number of distinct features used in the last 30 days, error events in the last 14 days, open support tickets, support tickets in the last 30 days, and days since last meaningful activity (an action like a login, a save, or an export, not just a passive ping). Only include accounts that are currently in an active subscription state. I will tell you the exact table and column names during setup. Use bind variables for any date math (today minus 14, today minus 30) and pass an explicit warehouse if my default warehouse is not set.
Step 2. Reason about risk per account. For each row, decide a churn risk tier of high, medium, low, or expanding, give a 0 to 100 numeric score (higher means more at risk), and write a one-sentence human-readable reason that names the specific signal driving the call. Examples of the reasoning you should do: an account with zero logins in the last 14 days, an open P1 ticket, and falling feature breadth is high risk; an account whose logins and feature adoption are climbing month over month is expanding; a steady-state account with normal usage and no support load is low. Do not invent signals that were not in the SQL result. If a signal is missing, say so in the reason.
Step 3. Write the scores back to HubSpot. Use Batch Upsert Contacts on HubSpot to update the matching contacts by email (idProperty = email), in batches of up to 100. Write three properties: churn_risk_tier (string: high, medium, low, expanding), churn_risk_score (number 0 to 100), and churn_risk_reason (short string, one or two sentences). These three custom contact properties should already exist in HubSpot. If you cannot find a contact for an email, skip that row and log it. Do not touch any other HubSpot properties.
Step 4. Post a Slack digest of the top 10 at-risk accounts. Use Send a Message on Slack to my CS channel (I will give you the channel ID during setup). Sort by churn risk score descending, take the top 10, and for each one include: account name, plan or MRR, risk tier and score, the one-line reason, and an @-mention of the account owner. Map the owner by looking up their Slack handle from their HubSpot owner email. Use Slack mrkdwn formatting (single asterisks for bold). Lead with a one-line summary like 'Daily churn watch: 10 accounts need a look today.' If there are zero high-risk accounts, post a single line saying 'No high-risk accounts today.' instead of a long empty digest.
Matching rule across systems: join Snowflake rows to HubSpot contacts by primary contact email (lowercased, trimmed). If my Snowflake schema uses a different shared key (for example a HubSpot contact ID stored as a column), ask me at setup which column to match on instead.
Be careful about scale. There may be thousands of active accounts. Stream through them in batches, respect HubSpot's batch upsert size of 100 per call, and do not include every account in the Slack post, only the top 10. Log how many accounts were scored, how many were upserted into HubSpot, and how many were skipped because no HubSpot contact existed.
What does this prompt do?
- Each morning, looks at product usage signals in your warehouse (logins in the last 14 and 30 days, feature adoption, error rates, support tickets, days since meaningful activity) and decides which active customers look at risk, healthy, or expanding.
- Writes a churn risk tier, a numeric score, and a one-line human reason back onto the matching HubSpot contact so sales and CS see it on the record.
- Posts a Slack digest of the top 10 at-risk accounts with the account owner @-mentioned, so the right people get nudged instead of a generic email blast.
- Runs every day on a schedule, no analyst pull required, no spreadsheet to keep up.
What do I need to use this?
- A Snowflake connection with a warehouse and role that can read your customer and product usage tables.
- A HubSpot login with permission to update contacts. Ideally with three custom contact properties ready: churn risk tier, churn risk score, and churn risk reason.
- A Slack workspace and a channel for the daily at-risk digest (for example, #cs-alerts or #pipeline).
- Contact owners set in HubSpot, or a way to map account owner emails to Slack handles so the digest can @-mention them.
How can I customize it?
- Change when it runs. Daily at 8am, weekday mornings only, or weekly on Monday for a lighter cadence.
- Tweak the signals and thresholds. For example, flag high risk only when an account has no logins for 14 days and an open support ticket, or include billing health.
- Swap the Slack channel, the number of accounts in the digest, or who gets @-mentioned (account owner, CS pod lead, or a single triage person).
FAQs
What if my product usage data is not in Snowflake?
Do I need to create custom fields in HubSpot first?
How does it match Snowflake rows to HubSpot contacts?
Will this overwrite churn scores we already track?
Can I skip the HubSpot write and just get the Slack digest?
Related templates
Pick a client and a date range to see sent, replies, meetings booked and the real deal value your cold email produced, campaign by campaign.
Work every partner tier decision from one board, with the revenue and commission numbers, clear promotion thresholds, and AI review briefs.
Open one board at month end to find every closed deal missing a commission, every commission with no deal behind it, and every amount that does not add up.
See warmup status, daily send volume, and bounce risk for every sending account in one grid, with the riskiest mailboxes floating to the top.
Open one board each morning to see which of your accounts are hiring, what they are building, and where your product fits the roles they just opened.
Review Introw partner payouts line by line, approve them from one board, raise the matching bill in Xero, and post every approval to Slack.
Stop guessing which customers are about to leave.
Connect Snowflake, HubSpot, and Slack once. Geni scores every active account every morning and tells the right people.