Paddle + Stripe Churn Cohort Alert: Flag MRR-at-Risk Accounts to Slack Before They Cancel

Catch churn signals — failed payments, seat drops, downgrades — before the cancellation hits, and get a ranked Slack digest every morning.

The flow
Stripe logo
Source
Stripe
Paddle logo
Process
Paddle
Pipedream logo
Process
Pipedream
Airtable logo
Process
Airtable
Slack logo
Destination
Slack

The stack in the order it runs — data flows from the source through to where it lands.

Why this stack

Most founders find out a customer churned when Stripe fires a 'subscription canceled' webhook. That's 30 days too late. The signal was already there: a failed payment retry, a dropped seat count, a switch from annual to monthly. Nobody is watching all three at once.

Pipedream is the right orchestrator because it handles multi-source fan-in cleanly — you subscribe to Stripe webhooks AND poll Paddle's API in the same workflow, no server required. n8n works too, but it needs self-hosting or a paid cloud plan. Pipedream's free tier covers the trigger volume most early-stage SaaS teams actually run.

Airtable is the correlation layer. Push at-risk signals from both gateways into a single base, tag each record with a risk tier (payment failure = high, downgrade = medium, usage drop = low), then a scheduled Pipedream step queries all high/medium records from the last 24 hours and formats them into a Slack digest.

Claude is optional but worth adding: before posting to Slack, pipe the raw Airtable records through a Claude prompt that writes one sentence of context per account — 'Acme Corp: 2 failed payment retries + dropped from 10 seats to 3 in 7 days.' That sentence saves your team 5 minutes of tab-switching per account. Skip this whole build if you have fewer than 50 active subscriptions — the noise won't justify it. Also skip if you're on one gateway only; the Paddle + Stripe dual-source complexity only pays off if you actually run both.

The stack (5)

  1. Stripe logo

    Global payments with first-class APIs.

    Events + Sigma let you wire billing into any ops report or alert.

  2. Paddle logo

    Merchant-of-record billing for SaaS.

    Handles global tax so you automate revenue ops without a compliance team.

  3. Pipedream logo

    Code-level workflows with hosted triggers.

    Drop into Node/Python mid-flow when no-code hits a wall.

  4. Airtable logo

    Relational database with a spreadsheet face. Operational memory for every workflow I run.

    The audit trail across views, automations, and webhooks is unmatched in no-code land.

  5. Slack logo

    Team chat where most ops alerts and reports land.

    The default place a small team already lives — pipe reports here instead of email nobody opens.

How it runs

  1. 1

    Set up Stripe webhook listener in Pipedream

    In Pipedream, create a new workflow with the Stripe app trigger. Subscribe to three events: `customer.subscription.updated`, `invoice.payment_failed`, and `customer.subscription.deleted`. In the Stripe dashboard under Developers → Webhooks, point the endpoint URL to your Pipedream source URL. Test with a sandbox subscription downgrade and confirm the payload arrives before moving on.

  2. 2

    Add a Paddle polling step for downgrade signals

    Paddle doesn't push webhook events for all plan changes reliably on older integrations. Add a second Pipedream workflow on a daily cron at 6 AM UTC. Use the HTTP Request step to call `GET https://vendors.paddle.com/api/2.0/subscription/users` with your `vendor_id` and `vendor_auth_code`. Filter the response for subscriptions where `plan_id` changed in the last 24 hours or `status` is `past_due`. You need yesterday's snapshot to do that comparison — write it to an Airtable table called `paddle_snapshot` on each run.

  3. 3

    Create the Airtable at-risk tracker base

    Create an Airtable base with a table called `churn_signals`. Fields: `account_id` (text), `account_name` (text), `gateway` (single select: Stripe / Paddle), `signal_type` (single select: payment_failed / downgrade / cancellation), `signal_date` (date), `risk_tier` (single select: high / medium / low), `notified` (checkbox), `context_note` (long text). Set `notified` default to false. This table is your dedup layer and your audit log. Never post the same signal twice.

  4. 4

    Write Pipedream steps to upsert signals into Airtable

    In both workflows — the Stripe webhook and the Paddle cron — add an Airtable step using the official Pipedream Airtable action 'Create Record' or 'Update Record'. Before inserting, search for an existing record matching `account_id` + `signal_type` + `signal_date` to block duplicates. Risk tier mapping: `payment_failed` → high, `downgrade` → medium, `cancellation` → high. Set `notified=false` on every new record.

  5. 5

    Generate account context with Claude

    Add a Claude step inside the daily cron workflow. After fetching all Airtable records where `notified=false` and `risk_tier` is high or medium, bundle them into a single prompt: 'For each account below, write one sentence summarizing the churn risk based on the signal type and any prior signals in the last 30 days. Be direct, no fluff.' Write the output back into each record's `context_note` field via Airtable update. Keep the prompt under 2,000 tokens — if you have more than 20 records, batch them.

  6. 6

    Format and post the Slack digest

    Add a Slack step at the end of the cron workflow. Use Block Kit: a header block reading '🚨 Churn Risk Digest — [date]', then one section block per account showing `account_name`, `signal_type`, a `risk_tier` badge (🔴 high, 🟡 medium), and `context_note`. Post to a dedicated #churn-alerts channel. If zero at-risk records exist, still post — send a green '✅ No new churn signals today' message. Silence is confusing.

  7. 7

    Mark records as notified and set a review SLA

    After the Slack step succeeds, loop through every record posted and update `notified=true` in Airtable. Add a `reviewed_by` text field and a `review_due` date field, auto-set to signal_date + 2 days. The SLA is simple: every high-tier record must have `reviewed_by` filled within 48 hours. You can wire a second Pipedream check that pings #churn-alerts if any high-tier record is older than 48 hours with `reviewed_by` still empty.

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 services

More like this