← All blueprints

Churn Radar Custom

blueprint · 5 agents · 3 MCP servers
902★ stars
118forks
4.6rating

What it does

A scheduled workflow. A usage agent pulls product signals from the warehouse — login recency, feature adoption, active seats — while a billing agent reads payment health from Stripe: failed charges, downgrades, cards about to expire. A scorer combines both into a churn-risk read for each account with the reasons behind it. The flow branches: at-risk accounts get a specific alert to their success owner with a suggested next play, and healthy accounts are logged so the trend stays visible. Run it weekly.

The cast · 5 agents

Loggerloggerfast

Write this run's risk reads to the tracking table so the trend over time stays visible, including the healthy accounts. Report the counts.

db.query_dsl
Scorerscorerreasoning

Combine usage and billing into a churn-risk read per account. Falling usage plus a failed charge is high risk; strong usage and clean billing is low. Return an object with, per account, an 'atRisk' boolean, the top reasons, and a suggested next play. Set the overall 'atRisk' flag if any account is at risk.

no tools · reasoning only
Alerteralerterfast

For each at-risk account, post a specific alert to its success owner: the account, the risk reasons, and the suggested play. One message per owner, grouped. No vague nudges.

chat.post_message
UsageusageAgentfast

Query the warehouse for product-usage signals per account in the segment: last login, active seats versus licensed, and adoption of core features. Report the numbers, read-only.

db.query_dsl
BillingbillingAgentfast

Read billing health per account from Stripe: recent failed charges, downgrades, and cards expiring soon. Report the facts per account.

pay.list_chargespay.list_subscriptions

Flow

⇉ parallel
Usageusage
Billingbilling
Scorerrisk
⑂ branch
risk.atRisk == true
Alertersummary

Interface

Inputs
segmentstring
Which book of accounts to scan, e.g. 'enterprise' or 'all'.
Outputs
summarystring
The at-risk accounts alerted and the healthy count, for this run.

Depends on · 3 MCP servers

io.github.supabase/postgres-mcpnot in the registry yetdb
com.stripe/payments-mcpnot in the registry yetpay
io.github.slack/slack-mcpnot in the registry yetchat
⤓ Export runnable code
# Generated by @socketcat/compiler for target: langgraph
# blueprint: com.socketcat/churn-radar v1.0.0   schema: socketcat.dev/blueprint/v0
# This code is yours. Edit it freely. The socketcat_runtime helper is optional and can be vendored.

from typing import TypedDict
from langgraph.graph import StateGraph, START, END
import socketcat_runtime as _rt

class State(TypedDict, total=False):
    segment: object
    summary: object
    usage: object
    billing: object
    risk: object

def node_Fork_0(state):
    return {}

def node_Join_1(state):
    return {}

def node_AgentInvoke_2(state):
    return _rt.run_agent("usageAgent", state, {"segment":"segment"}, "usage")

def node_AgentInvoke_3(state):
    return _rt.run_agent("billingAgent", state, {"segment":"segment"}, "billing")

def node_AgentInvoke_4(state):
    return _rt.run_agent("scorer", state, {"usage":"usage","billing":"billing"}, "risk")

def node_Branch_5(state):
    return {}

def node_Join_6(state):
    return {}

def node_AgentInvoke_7(state):
    return _rt.run_agent("alerter", state, {"risk":"risk"}, "summary")

def node_AgentInvoke_8(state):
    return _rt.run_agent("logger", state, {"risk":"risk"}, "summary")

def route_Branch_5(state):
    if _rt.cond("risk.atRisk == true", state): return "AgentInvoke_7"
    return "AgentInvoke_8"

def build():
    b = StateGraph(State)
    b.add_node("Fork_0", node_Fork_0)
    b.add_node("Join_1", node_Join_1)
    b.add_node("AgentInvoke_2", node_AgentInvoke_2)
    b.add_node("AgentInvoke_3", node_AgentInvoke_3)
    b.add_node("AgentInvoke_4", node_AgentInvoke_4)
    b.add_node("Branch_5", node_Branch_5)
    b.add_node("Join_6", node_Join_6)
    b.add_node("AgentInvoke_7", node_AgentInvoke_7)
    b.add_node("AgentInvoke_8", node_AgentInvoke_8)
    b.add_edge(START, "Fork_0")
    b.add_edge("Fork_0", "AgentInvoke_2")
    b.add_edge("Fork_0", "AgentInvoke_3")
    b.add_edge("Join_1", "AgentInvoke_4")
    b.add_edge("AgentInvoke_2", "Join_1")
    b.add_edge("AgentInvoke_3", "Join_1")
    b.add_edge("AgentInvoke_4", "Branch_5")
    b.add_conditional_edges("Branch_5", route_Branch_5, {"AgentInvoke_7": "AgentInvoke_7", "AgentInvoke_8": "AgentInvoke_8"})
    b.add_edge("Join_6", END)
    b.add_edge("AgentInvoke_7", "Join_6")
    b.add_edge("AgentInvoke_8", "Join_6")
    return b.compile()

INPUTS = ["segment"]
OUTPUTS = ["summary"]

if __name__ == "__main__":
    _rt.main(build, INPUTS, OUTPUTS)
blueprint.json (the portable format)
{
  "id": "com.socketcat/churn-radar",
  "flow": {
    "type": "sequence",
    "blocks": [
      {
        "type": "parallel",
        "blocks": [
          {
            "in": {
              "segment": "segment"
            },
            "out": "usage",
            "use": "usageAgent",
            "type": "agent"
          },
          {
            "in": {
              "segment": "segment"
            },
            "out": "billing",
            "use": "billingAgent",
            "type": "agent"
          }
        ]
      },
      {
        "in": {
          "usage": "usage",
          "billing": "billing"
        },
        "out": "risk",
        "use": "scorer",
        "type": "agent"
      },
      {
        "else": [
          {
            "in": {
              "risk": "risk"
            },
            "out": "summary",
            "use": "logger",
            "type": "agent"
          }
        ],
        "type": "branch",
        "cases": [
          {
            "when": "risk.atRisk == true",
            "blocks": [
              {
                "in": {
                  "risk": "risk"
                },
                "out": "summary",
                "use": "alerter",
                "type": "agent"
              }
            ]
          }
        ]
      }
    ]
  },
  "tags": [
    "customer-success",
    "retention",
    "scheduled",
    "revenue"
  ],
  "title": "Churn Radar",
  "agents": {
    "logger": {
      "model": {
        "hint": "fast"
      },
      "title": "Logger",
      "tools": [
        "db.query_dsl"
      ],
      "instructions": "Write this run's risk reads to the tracking table so the trend over time stays visible, including the healthy accounts. Report the counts."
    },
    "scorer": {
      "model": {
        "hint": "reasoning"
      },
      "title": "Scorer",
      "output": {
        "type": "object",
        "required": [
          "atRisk"
        ],
        "properties": {
          "atRisk": {
            "type": "boolean"
          },
          "accounts": {
            "type": "array"
          }
        }
      },
      "instructions": "Combine usage and billing into a churn-risk read per account. Falling usage plus a failed charge is high risk; strong usage and clean billing is low. Return an object with, per account, an 'atRisk' boolean, the top reasons, and a suggested next play. Set the overall 'atRisk' flag if any account is at risk."
    },
    "alerter": {
      "model": {
        "hint": "fast"
      },
      "title": "Alerter",
      "tools": [
        "chat.post_message"
      ],
      "instructions": "For each at-risk account, post a specific alert to its success owner: the account, the risk reasons, and the suggested play. One message per owner, grouped. No vague nudges."
    },
    "usageAgent": {
      "model": {
        "hint": "fast"
      },
      "title": "Usage",
      "tools": [
        "db.query_dsl"
      ],
      "instructions": "Query the warehouse for product-usage signals per account in the segment: last login, active seats versus licensed, and adoption of core features. Report the numbers, read-only."
    },
    "billingAgent": {
      "model": {
        "hint": "fast"
      },
      "title": "Billing",
      "tools": [
        "pay.list_charges",
        "pay.list_subscriptions"
      ],
      "instructions": "Read billing health per account from Stripe: recent failed charges, downgrades, and cards expiring soon. Report the facts per account."
    }
  },
  "estate": {
    "risk": {
      "type": "object",
      "description": "The scorer's per-account risk read, including an 'atRisk' flag."
    },
    "usage": {
      "type": "object",
      "description": "Per-account product-usage signals from the warehouse."
    },
    "billing": {
      "type": "object",
      "description": "Per-account billing health from Stripe."
    }
  },
  "$schema": "socketcat.dev/blueprint/v0",
  "authors": [
    {
      "url": "https://socketcat.com",
      "name": "SocketCat"
    }
  ],
  "license": "MIT",
  "servers": [
    {
      "ref": "io.github.supabase/postgres-mcp",
      "alias": "db"
    },
    {
      "ref": "com.stripe/payments-mcp",
      "alias": "pay"
    },
    {
      "ref": "io.github.slack/slack-mcp",
      "alias": "chat"
    }
  ],
  "summary": "Combines product-usage and billing signals into a per-account churn-risk read, then alerts the owner on at-risk accounts.",
  "targets": [
    "langgraph",
    "*"
  ],
  "version": "1.0.0",
  "interface": {
    "inputs": {
      "segment": {
        "type": "string",
        "description": "Which book of accounts to scan, e.g. 'enterprise' or 'all'."
      }
    },
    "outputs": {
      "summary": {
        "type": "string",
        "description": "The at-risk accounts alerted and the healthy count, for this run."
      }
    }
  },
  "extensions": {
    "dev.langgraph": {
      "checkpointer": "memory"
    }
  },
  "description": "A scheduled workflow. A usage agent pulls product signals from the warehouse — login recency, feature adoption, active seats — while a billing agent reads payment health from Stripe: failed charges, downgrades, cards about to expire. A scorer combines both into a churn-risk read for each account with the reasons behind it. The flow branches: at-risk accounts get a specific alert to their success owner with a suggested next play, and healthy accounts are logged so the trend stays visible. Run it weekly."
}

More in Sales & marketing

Browse all →

Lead Enrichment & Outreach

com.socketcat/lead-enrichment
LangGraph

A new lead is enriched from the web and your CRM at once, scored against your ICP, and — if it fits — gets a tailored first email you approve before send.

13104 servers
189

Content Factory

com.socketcat/content-factory
CrewAI

A four-agent editorial crew: research the topic, write the draft, edit it against a quality bar, and file the finished piece in Notion ready to publish.

12302 servers
167

Blog Repurposer

com.socketcat/blog-repurposer
LangGraph

Takes one finished post and rewrites it for three channels at once — a thread, a newsletter section, a LinkedIn post — each in that channel's native voice, filed to Notion.

9871 server
121