BlogCompose

Churn doesn't announce itself. My Monday Slack digest does it instead.

Every founder finds out about churn the same way: the cancellation email. By then the user has been gone for weeks - the decision happened earlier, quietly, in their usage. So I made the metering tables write me a memo.

The Monday digest

Every founder finds out about churn the same way: the cancellation email. By then the user has been gone for weeks - the cancel is the paperwork, not the decision. The decision happened earlier, quietly, in their usage: the daily user who went weekly, the power feature that stopped appearing, the API calls that flatlined. That decay is fully visible in metering data. The problem was never detection - it is that nobody reads per-user usage tables on a Monday morning. So I made the tables write me a memo. Vevee meters every user's AI usage (@vevee/sdk), and its compose() generates structured output from any user's history - including a churn-risk assessment. A Monday cron runs it over my paying users:

The compose type's prompt: "Assess churn risk from this user's usage trend. Compare recent activity to their own baseline, not to other users. Cite only concrete evidence with numbers. Infer the most probable cause from the pattern. Suggest exactly one action a founder can take this week. If the data is ambiguous, say 'cooling', not 'at_risk' - false alarms kill this report's credibility." Data sources: user usage, user events, cohort comparison.
import { createClient } from "@vevee/sdk";

const vevee = createClient({ apiKey: process.env.VEVEE_SECRET_KEY! });

interface ChurnAssessment {
  risk: "healthy" | "cooling" | "at_risk" | "gone_quiet";
  evidence: string[];      // concrete usage facts, with numbers
  probableCause: string;   // best inference from the pattern
  suggestedAction: string; // one thing, doable this week
}

const flagged: Array<{ email: string; a: ChurnAssessment }> = [];

for (const user of payingUsers) {
  const result = await vevee.compose<ChurnAssessment>(
    "churn-assessment",
    user.id,
    { plan: user.plan, mrr: user.mrr }
  );

  if (result.status === "generated" && result.output.risk !== "healthy") {
    flagged.push({ email: user.email, a: result.output });
  }
}

await postToSlack("#health", formatDigest(flagged)); // sorted by MRR at risk

What Monday looks like now

Here is what lands in #health. Two things make this readable where a dashboard is not. First, the comparison is to each user's own baseline - 50 events/week is healthy for one account and a 90% collapse for another; absolute thresholds cannot see that, the per-user composition can. Second, it arrives ranked and reasoned - not "engagement score: 42" but "she failed three times and stopped," which is a sentence a human acts on.

  • #health - Week 24 · 3 accounts flagged · $94 MRR at risk
  • dana@- (Pro, $15) - at_risk. Was 40 generations/week for 2 months → 4 last week → 0 this week. Last session ended on three failed style: "anime" requests in a row.
  • Probable cause: hit a quality wall on her main use case, not loss of interest - frequency was stable until the failures.
  • Action: personal email asking about the anime-style results; fix or workaround likely saves the account.
  • team@- (Max, $49) - cooling. API volume -30% over 3 weeks; dashboard logins unchanged. Pattern fits their end-users dropping, not them leaving.
  • Action: offer a 20-min call about their conversion funnel - their churn is your churn one step removed.

Close the loop or it's theater

Each flagged account gets the suggested action; the outcome is one analytics event away (same SDK, reserved event names). After a couple of months you know your save rate per risk tier and per action type - which converts "founder gut feel" into the retention playbook you will someday hand a hire.

await vevee.capture({ distinctId: userId, event: "feature_used",
  properties: { source: "churn_save_action" } });
// vs. the eventual subscription_cancelled if the save fails

Cost and trust

A weekly sweep across a few hundred paying users costs cents - every result reports usage.costMicroUsd, and the dashboard's monthly compose budget hard-caps the spend. Users who opted out of AI personalization return opted_out and simply don't get assessed; for a consent-scoped system that's the correct trade, and you can still eyeball their raw charts by hand. The cancellation email is a lagging indicator with a 100% detection rate and a 0% save rate. The Monday digest is the opposite trade. Take it.

See your own numbers in two minutes.Add the SDK and your users, costs and revenue fill in on the next request.
Join waitlist

Keep reading

ShareCopy a link to this post.

Set your first limit in about five minutes.

50k operations free every month. No card required.
Join waitlist