Intercom CSAT + Stripe Churn Signal: Daily At-Risk Account Alert to Slack via Zapier
Every morning, surface every paying account that rated support 'Unhappy' in the last 24 hours — with their Stripe MRR attached — so your CS team can call before the cancel request lands.
The stack in the order it runs — data flows from the source through to where it lands.
A bad support interaction is one of the highest-predictability churn signals you have. But most teams see it in a weekly CSAT report, by which point the user has already cancelled or gone silent. For monthly plans, the median time between a negative support interaction and a cancellation is under 72 hours. This workflow closes that gap — daily, automatic, with enough Stripe context to prioritize who to call first.
Zapier is the right tool here because the trigger is an Intercom event, not a scheduled poll. Zapier's native Intercom integration has a 'New Conversation Rating' trigger that fires in near real-time. n8n and Make require polling Intercom's API on a schedule, which introduces lag. For a time-sensitive churn signal, real-time trigger beats scheduled pull every time.
The Stripe enrichment step is what makes this actionable instead of just informational. A negative CSAT from a $500/month customer deserves a phone call. The same score from a free trial user can wait for an email. Without MRR context attached to each alert, your success team either triages manually or applies the same response to everyone — both are expensive mistakes.
Airtable sits in the middle as a deduplication and batching layer. Zapier fires once per CSAT event, so without a buffer you'd get spammed if one customer has three bad conversations in a day. Airtable records each event; a separate scheduled Zap reads all unnotified records once at 08:00 and sends one batched Slack digest. Not batching event-triggered Zaps is the most common mistake I see with this kind of workflow. Two hard limits to know before you build: if your support volume exceeds 200 CSAT responses per day, Zapier's task count gets expensive — move to n8n with a webhook receiver instead. And this whole flow requires your Intercom contacts to have an `email` attribute that matches the email on the Stripe customer record. If your CRM hygiene is inconsistent, the Stripe lookup will fail silently.
The stack (5)
How it runs
- 1
Set up the Airtable deduplication table
Create an Airtable base called 'Churn Risk Signals' with a table called 'CSAT Alerts'. Fields: Conversation ID (Single Line Text, unique), Customer Email (Email), CSAT Score (Number), Conversation Summary (Long Text), Stripe Customer ID (Single Line Text), MRR (Currency), Plan Name (Single Line Text), Notified (Checkbox, default false), Created At (Date/Time). This table is the buffer between the real-time Intercom trigger and the daily Slack batch. Get this right before you build either Zap — everything downstream depends on this schema.
- 2
Build Zap 1: Intercom CSAT → Airtable record
Create a new Zap. Trigger: Intercom → 'New Conversation Rating'. Filter: only proceed if `Rating` equals 'Unhappy' — that's Intercom's negative score label, but confirm the exact string in your Intercom account under Settings → Conversation Ratings before you map it. Action: Airtable → 'Create Record' in the CSAT Alerts table. Map Conversation ID, the customer's email from `conversation.user.email`, CSAT Score, and a truncated version of `conversation.last_reply.body` as the summary. Leave the Stripe fields blank for now — the next step fills them.
- 3
Enrich with Stripe customer data in Zap 1
Add a second action in the same Zap: 'Code by Zapier' (Python). Use the `requests` library to call `https://api.stripe.com/v1/customers?email={{customer_email}}&limit=1` with your Stripe secret key as Bearer token. Extract `customers.data[0].id` as Stripe Customer ID. Then call `/v1/subscriptions?customer={{stripe_customer_id}}&status=active&limit=1` and extract `plan.nickname` and `plan.amount` divided by 100. Return those as output fields. Add a final Airtable 'Update Record' step to write Stripe Customer ID, MRR, and Plan Name back to the row you created in step 2.
- 4
Build Zap 2: daily batch digest trigger
Create a second Zap. Trigger: Schedule by Zapier → every day at 08:00 in your team's timezone. This Zap reads all unnotified CSAT alerts from Airtable and sends one Slack message. One scheduled Zap reading a filtered Airtable view is cleaner than triggering on each individual record — it means one Slack message per day, not one per bad conversation.
- 5
Read unnotified records from Airtable in Zap 2
Add an Airtable 'Find Records' action. Filter by formula: `AND({Notified} = FALSE(), {CSAT Score} <= 2, IS_AFTER({Created At}, DATEADD(TODAY(), -1, 'days')))`. Sort by MRR descending so the highest-value accounts appear first in the Slack message. Zapier's Airtable Find Records returns up to 100 rows. If you're regularly hitting that ceiling, this tool isn't right for your volume.
- 6
Format and send the Slack alert
Add a 'Code by Zapier' step before the Slack action to iterate over the Airtable records array and build a formatted string. Each line: `• {{email}} ({{plan_name}} — ${{mrr}}/mo) — CSAT: {{score}} — "{{summary_truncated}}"}`. Header: `🔴 *At-Risk Accounts — {{today's date}}* ({{count}} accounts, ${{total_mrr_at_risk}}/mo at risk)`. Then add a Slack 'Send Channel Message' action pointing to `#cs-churn-risk`. If the Airtable query returns zero records, use a Zapier Filter step to skip the Slack send entirely — no records, no message.
- 7
Mark records as notified after Slack delivery
After the Slack send, update each record in Airtable: set `Notified = true`. Use Zapier's 'Looping by Zapier' add-on to handle multiple records, or do it inside the Code by Zapier step with a for-loop calling the Airtable PATCH API directly. Skip this step and the same accounts will show up in tomorrow's digest even after your team has already called them. That's not a minor bug — it destroys trust in the alert fast.
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.