Customer churn predictor
Nightly: score accounts, write a CS queue, post top 10 to Slack.
p95 latency on /api/checkout crossed 1200ms for 6 min. Correlated deploy: web@d41f2a. Suggested rollback command attached.
By the time you notice an account has stopped logging in, they've already started evaluating competitors.
Every morning your CS team works a ranked queue. The top 10 are in Slack with the three signals that flagged them.
Ingredients & skills
- ANTHROPIC_API_KEY
- DATABASE_URL
- SLACK_WEBHOOK_URL
- Anthropic
- Postgres
- Slack
- postgres-mcp
How it works
SQL pulls product-usage features for every paying account, Claude scores churn risk 0–100, results go into a `cs_queue` table and the top 10 to a Slack channel.
1 — Feature query
Six features, all derived. Don't pass raw event tables to the model.
select
account_id,
date_part('day', now() - max(ts)) as days_since_last_event,
count(*) filter (where ts > now() - interval '7 days') as events_7d,
count(*) filter (where ts > now() - interval '30 days') as events_30d,
count(distinct user_id) filter (where ts > now() - interval '7 days') as wau,
bool_or(plan = 'enterprise') as is_enterprise,
sum(case when event = 'export' then 1 else 0 end) as exports
from events group by 1;2 — Scoring agent
Batch 100 accounts per call. One scored row out per row in.
const scored = await claude.tools.call("score_batch", { features });
await db.query("insert into cs_queue (account_id, risk, reasons, scored_at) select * from unnest($1::text[], $2::int[], $3::jsonb[], $4::timestamptz[])",
[scored.map((s) => s.account_id), scored.map((s) => s.risk), scored.map((s) => s.reasons), scored.map(() => new Date())]);3 — Top 10 to Slack
Quick, low-noise. CS pages own the deeper queue.
const top = await db.query("select account_id, risk, reasons from cs_queue where scored_at::date = current_date order by risk desc limit 10");
await fetch(process.env.SLACK_WEBHOOK_URL!, { method: "POST", body: JSON.stringify({ text: format(top.rows) }) });The button above runs the same command with your saved config. This is the raw CLI form.
locker schedule churn-predictor agent.ts --cron '0 6 * * *'