name: account-risk-early-warning description: Identifies accounts showing warning signs of churn by analyzing activity trends, support cases, and engagement signals. Scores risk and prioritizes intervention targets. Use when user asks "which accounts are at risk", "churn risk analysis", "find accounts that might leave", "customer health check", "at-risk customers", "retention warning signs", or "account health score". metadata: author: Dataverse version: 1.0.0 category: customer-success
Account Risk Early Warning
Retaining existing customers is more cost-effective than acquiring new ones. This skill monitors key accounts for early warning signals that indicate potential churn or relationship issues, enabling proactive intervention before problems escalate.
Instructions
Step 1: Determine Scope
When user asks "Which of my top accounts are showing warning signs?":
1.1 Define Account Set:
Options:
A) All active accounts owned by user
B) Specific account list (e.g., enterprise tier)
C) Accounts with revenue above threshold
D) Accounts up for renewal in next X months
1.2 Query Target Accounts:
SELECT accountid, name, revenue, numberofemployees, industrycode,
createdon, ownerid, customertypecode, territoryid,
openrevenue, openrevenue_date
FROM account
-- Note: openrevenue and openrevenue_date are rollup fields that may not exist in all orgs.
-- If absent, calculate open pipeline value by querying the opportunity table directly.
WHERE statecode = 0
AND ownerid = '[current_user_id]' -- or specific criteria
ORDER BY revenue DESC
Step 2: Analyze Risk Signals for Each Account
Important: Dataverse SQL Limitations Dataverse SQL does NOT support: subqueries, DATEADD(), GETUTCDATE(), HAVING, DISTINCT, UNION, CASE statements. Use separate queries and calculate date filters programmatically (e.g., calculate '2025-09-01' for 6 months ago).
2.1 Activity Trend Analysis Query recent activities for each account (run separately per account):
SELECT activityid, activitytypecode, createdon, statecode
FROM activitypointer
WHERE regardingobjectid = '[accountid]'
AND createdon > '2025-09-01'
ORDER BY createdon DESC
Calculate Activity Metrics:
For each account:
- Total activities (last 6 months)
- Activities per month trend
- Activity types breakdown
- Days since last activity
- Comparison to account's historical average
Activity Risk Indicators:
| Signal | Warning | Critical |
|---|---|---|
| Days Since Last Activity | 30+ days | 60+ days |
| Month-over-Month Decline | 25%+ decline | 50%+ decline |
| No Meetings/Calls | 45+ days | 90+ days |
| Only Automated Emails | 30+ days | 60+ days |
2.2 Support Case Pattern Analysis Query recent cases (run per account, calculate date filter programmatically):
SELECT incidentid, title, createdon, prioritycode, severitycode,
statecode, statuscode, caseorigincode, customersatisfactioncode,
msdyn_casesentiment, escalatedon
FROM incident
WHERE customerid = '[accountid]'
AND createdon > '2025-09-01'
ORDER BY createdon DESC
Calculate Case Metrics:
For each account:
- Total cases (last 6 months)
- Cases per month trend
- Case severity distribution
- Average resolution time
- Escalation count
- Customer satisfaction scores (if available)
- Sentiment analysis (msdyn_casesentiment)
Case Risk Indicators:
| Signal | Warning | Critical |
|---|---|---|
| Case Volume Increase | 50%+ increase | 100%+ increase |
| High Severity Cases | 2+ in 30 days | 5+ in 30 days |
| Escalated Cases | 1 in 60 days | 2+ in 60 days |
| Negative Sentiment | Score 9 (Slightly negative) | Score 7-8 (Very negative / Negative) |
| Low Satisfaction | Score 2-3 | Score 1 (Very Dissatisfied) |
| Unresolved Cases | 3+ open > 7 days | 5+ open > 14 days |
2.3 Opportunity Velocity Analysis Query opportunity history (per account, calculate date filter programmatically):
SELECT opportunityid, name, estimatedvalue, actualvalue,
statecode, statuscode, createdon, actualclosedate,
salesstage, msdyn_forecastcategory
FROM opportunity
WHERE accountid = '[accountid]'
AND createdon > '2024-03-01'
ORDER BY createdon DESC
Calculate Opportunity Metrics:
For each account:
- New opportunities (last 6 months vs prior 6 months)
- Win rate trend
- Average deal size trend
- Pipeline coverage
- Time since last won deal
- Lost opportunities (recent)
Opportunity Risk Indicators:
| Signal | Warning | Critical |
|---|---|---|
| No New Opportunities | 90+ days | 180+ days |
| Declining Win Rate | 20%+ decline | 50%+ decline |
| Declining Deal Size | 25%+ decline | 50%+ decline |
| Recent Lost Deals | 1 in 60 days | 2+ in 60 days |
| Stalled Pipeline | 60+ days no movement | 90+ days |
2.4 Engagement Pattern Analysis Analyze contact engagement:
SELECT c.contactid, c.fullname, c.jobtitle, c.accountrolecode
FROM contact c
WHERE c.accountid IN ([target account ids])
AND c.statecode = 0
SELECT a.regardingobjectid, COUNT(*) as activity_count,
MAX(a.createdon) as last_activity
FROM activitypointer a
JOIN contact c ON a.regardingobjectid = c.contactid
WHERE c.accountid IN ([target account ids])
AND a.createdon >= '[6_months_ago]'
GROUP BY a.regardingobjectid
Engagement Risk Indicators:
| Signal | Warning | Critical |
|---|---|---|
| Key Contact Disengaged | 45+ days no response | 90+ days |
| Champion Gone Dark | 30+ days | 60+ days |
| Executive Engagement Drop | 50%+ decline | 75%+ decline |
| New Decision Maker | Not yet engaged | 60+ days not engaged |
| Contact Left Company | Detected | Champion left |
2.5 Relationship Signals from Notes Analyze annotations and activity descriptions:
SELECT a.objectid, a.subject, a.notetext, a.createdon
FROM annotation a
WHERE a.objecttypecode = 'account'
AND a.objectid IN ([target account ids])
AND a.createdon >= '[6_months_ago]'
ORDER BY a.createdon DESC
Keyword Detection: Look for risk keywords in notes:
- Negative: "unhappy", "frustrated", "dissatisfied", "complaint", "issue", "problem", "leaving", "cancel", "competitor"
- Competitive: "evaluating alternatives", "RFP", "comparing options", "other vendors"
- Organizational: "budget cut", "freeze", "layoffs", "restructuring", "merger", "acquisition"
- Turnover: "leaving company", "new role", "replacement", "transition"
2.6 Renewal and Contract Signals If contract/renewal data available:
Query opportunities or custom entities for:
- Upcoming renewal dates
- Contract end dates
- Renewal discussions (or lack thereof)
Renewal Risk Indicators:
| Signal | Warning | Critical |
|---|---|---|
| Renewal in 90 days | No discussion started | Negative feedback received |
| Renewal in 60 days | No commitment | Competitor being evaluated |
| Renewal in 30 days | Not verbal confirmed | At risk / declining |
Step 3: Calculate Composite Risk Score
Risk Score Model (0-100, higher = more risk):
| Risk Category | Weight | Max Points |
|---|---|---|
| Activity Decline | 20% | 20 |
| Support Case Patterns | 25% | 25 |
| Opportunity Velocity | 20% | 20 |
| Engagement Patterns | 20% | 20 |
| Relationship Signals | 15% | 15 |
Score Interpretation:
- 0-25: Healthy - No significant risk signals
- 26-50: Monitor - Early warning signs, watch closely
- 51-75: At Risk - Multiple warning signals, intervention needed
- 76-100: Critical - Immediate action required
Step 4: Prioritize Intervention Targets
Sort accounts by:
- Risk Score (highest first)
- Revenue Impact (larger accounts prioritized)
- Renewal Proximity (sooner renewals first)
- Relationship Investment (longer tenure/more history)
Calculate Priority Score:
priority = risk_score * 0.5 + revenue_weight * 0.3 + renewal_urgency * 0.2
Where:
- risk_score: 0-100 from Step 3
- revenue_weight: Normalized account revenue
- renewal_urgency: Days until renewal (inverted scale)
Step 5: Generate Risk Report
Output Format:
ACCOUNT RISK EARLY WARNING REPORT
════════════════════════════════════════════════════
Generated: [Date]
Scope: [User]'s Accounts
Accounts Analyzed: [N]
════════════════════════════════════════════════════
RISK SUMMARY
════════════════════════════════════════════════════
Critical Risk (76-100): [N] accounts $[revenue at risk]
At Risk (51-75): [N] accounts $[revenue at risk]
Monitor (26-50): [N] accounts $[revenue at risk]
Healthy (0-25): [N] accounts $[revenue value]
TOTAL REVENUE AT RISK: $[sum of Critical + At Risk]
════════════════════════════════════════════════════
PRIORITY INTERVENTION LIST
════════════════════════════════════════════════════
1. 🔴 CONTOSO CORPORATION
Risk Score: 85/100 (Critical)
Annual Revenue: $500,000
Renewal: 45 days
─────────────────────────────────────────────────
WARNING SIGNALS:
⚠️ Activity Decline: 67% fewer touchpoints vs prior quarter
⚠️ Support Cases: 8 cases in 30 days (up 300% from average)
⚠️ Escalation: 2 escalated cases in past month
⚠️ Sentiment: Case sentiment trending negative (avg 7.5)
⚠️ Competitor: "Evaluating alternatives" mentioned in notes
⚠️ Contact: VP Champion hasn't responded in 45 days
DETAILED ANALYSIS:
Activity Trend:
• 6 months ago: 12 activities/month
• Current: 4 activities/month
• Trend: ↓ 67% decline
Support Cases:
• Total (6 mo): 15 cases
• Last 30 days: 8 cases (spike)
• High Severity: 3 cases
• Avg Resolution: 5.2 days (above SLA)
Opportunity Status:
• No new opportunities in 120 days
• Last won deal: 8 months ago
• Pipeline: $0 (no active deals)
Key Contacts:
• Sarah Chen (VP Ops) - Last contact: 45 days ago ⚠️
• John Smith (IT Dir) - Last contact: 12 days ago ✓
RECOMMENDED INTERVENTIONS:
Priority 1 (Immediate):
📞 Executive outreach from sales leadership to Sarah Chen
• Acknowledge support issues
• Request relationship review meeting
• Bring solutions, not sales pitch
Priority 2 (This Week):
📋 Support case review with CS team
• Identify root cause of case spike
• Escalate to product/engineering if needed
• Prepare remediation plan
Priority 3 (Within 2 Weeks):
📅 Renewal discussion meeting
• Don't wait - engage on renewal now
• Address concerns before they finalize decision
• Consider retention incentives if needed
─────────────────────────────────────────────────
2. 🟠 FABRIKAM INDUSTRIES
Risk Score: 62/100 (At Risk)
Annual Revenue: $250,000
Renewal: 90 days
─────────────────────────────────────────────────
WARNING SIGNALS:
⚠️ Champion Left: Primary contact left company 30 days ago
⚠️ New Decision Maker: Replacement not yet engaged
⚠️ Activity: 45 days since meaningful engagement
RECOMMENDED INTERVENTIONS:
Priority 1: Identify and engage new decision maker
• Research LinkedIn for replacement
• Get warm intro from remaining contacts
• Schedule introductory meeting
[Continue for additional at-risk accounts...]
Step 6: Create Intervention Tasks
For each high-priority account, offer to create:
Use create_record with tablename: task
{
"subject": "⚠️ Risk Intervention: [Account Name]",
"description": "Risk Score: [X]/100\n\nKey Warning Signals:\n- [Signal 1]\n- [Signal 2]\n\nRecommended Actions:\n1. [Action 1]\n2. [Action 2]",
"regardingobjectid": "[accountid]",
"scheduledend": "[today or tomorrow]",
"prioritycode": 1 (High)
}
Add note to account record:
Use create_record with tablename: annotation
{
"subject": "Risk Assessment - [Date]",
"notetext": "Account flagged as [Risk Level].\n\nWarning Signals:\n[Details]\n\nIntervention Plan:\n[Actions]",
"objectid": "[accountid]",
"objecttypecode": "account"
}
Dataverse Tables Used
| Table | Purpose |
|---|---|
account | Primary entity for risk analysis |
activitypointer | Activity trend analysis |
phonecall | Engagement tracking |
appointment | Meeting frequency |
email | Communication patterns |
incident | Support case patterns |
opportunity | Revenue velocity |
contact | Stakeholder engagement |
annotation | Notes for signal detection |
task | Create intervention tasks |
Key Fields Reference
account:
revenue(MONEY) - Account value for prioritizationownerid(OWNER) - Account owner for filteringcreatedon(DATETIME) - Tenure calculationcustomertypecode(CHOICE) - Relationship typeopenrevenue(MONEY) - Total open pipeline value (rollup field; availability depends on org configuration)msdyn_accountkpiid(LOOKUP → msdyn_accountkpiitem) - Relationship analytics KPIstatecode(STATE) - Active(0), Inactive(1)
msdyn_accountkpiitem (Sales Insights - Relationship Analytics):
msdyn_relationshiphealthscorevalue(INT) - Health score (0-100)msdyn_relationshiphealthscorestate(CHOICE) - Good(0), Fair(1), Poor(2)msdyn_relationshiphealthtrend(CHOICE) - Improving(0), Steady(1), Declining(2), Not enough info(3)msdyn_emailssent(INT) - Outgoing emails countmsdyn_emailsreceived(INT) - Incoming emails countmsdyn_phonecallsmade(INT) - Outbound calls countmsdyn_phonecallsreceived(INT) - Inbound calls countmsdyn_meetingssent(INT) - Meetings scheduled countmsdyn_lastactivitybyteam(DATETIME) - Last team outreachmsdyn_nextactivitybyteam(DATETIME) - Next scheduled activitymsdyn_facetimewithcustomer(DURATION) - Total meeting timemsdyn_timespentbyteam(DURATION) - Total time investedmsdyn_averagefirstresponsetimebyusinhrs(FLOAT) - Avg response time (hours)
incident:
prioritycode(CHOICE) - High(1), Normal(2), Low(3)severitycode(CHOICE) - Severity levelcasetypecode(CHOICE) - Question(1), Problem(2), Request(3)caseorigincode(CHOICE) - Source channelcustomersatisfactioncode(CHOICE) - Very Dissatisfied(1), Dissatisfied(2), Neutral(3), Satisfied(4), Very Satisfied(5)msdyn_casesentiment(CHOICE) - Sentiment scale (lower = more negative): Very negative(7), Negative(8), Slightly negative(9), Neutral(10), Slightly positive(11), Positive(12), Very positive(13)escalatedon(DATETIME) - Escalation trackingstatecode(STATE) - Active(0), Resolved(1), Cancelled(2)statuscode(STATUS) - In Progress(1), On Hold(2), Waiting(3), Researching(4) [Active]; Problem Solved(5), Info Provided(1000) [Resolved]; Cancelled(6), Merged(2000) [Cancelled]
activitypointer:
activitytypecode(NVARCHAR) - Type: phonecall, email, appointment, taskcreatedon(DATETIME) - Activity dateregardingobjectid(LOOKUP) - Polymorphic link to account, contact, etc.statecode(STATE) - Open(0), Completed(1), Canceled(2), Scheduled(3-appointments)
Risk Signal Weights (Configurable)
| Signal Category | Default Weight | Adjustable Range |
|---|---|---|
| Activity Decline | 20% | 10-30% |
| Support Cases | 25% | 15-35% |
| Opportunity Velocity | 20% | 10-30% |
| Engagement Patterns | 20% | 10-30% |
| Relationship Signals | 15% | 10-25% |
Best Practices
- Early intervention wins: Act on Monitor-level risks before they become Critical
- Don't assume: Verify signals with direct customer conversation
- Cross-functional response: Involve Success, Support, and Sales in intervention
- Document everything: Track intervention actions for pattern learning
- Regular cadence: Run risk analysis weekly for proactive management
Examples
Example 1: Weekly Account Health Check
User says: "Which of my accounts are at risk?"
Actions:
- Query all active accounts owned by user
- Analyze activity trends, support cases, opportunity velocity for each
- Calculate composite risk score
- Rank accounts by risk level
Result:
ACCOUNT RISK SUMMARY
Critical (2): Immediate intervention required
Warning (5): Schedule check-in this week
Monitor (8): Watch for changes
Healthy (35): No action needed
CRITICAL ACCOUNTS:
1. Contoso Ltd - Risk Score: 85/100
- 60+ days since last activity
- 3 escalated support cases
- Renewal in 45 days
ACTION: Executive outreach immediately
2. Fabrikam Inc - Risk Score: 78/100
- Activity declined 70% vs prior quarter
- Champion contact left company
ACTION: Re-establish relationships
Example 2: Specific Account Health Analysis
User says: "Check the health of the Northwind account"
Actions:
- Retrieve Northwind account details
- Analyze all risk signals in depth
- Provide detailed breakdown with recommendations
Result:
NORTHWIND TRADERS - HEALTH ASSESSMENT
Overall Risk: WARNING (62/100)
ACTIVITY ANALYSIS:
- Last interaction: 28 days ago (email)
- Trend: Declining (45% fewer touchpoints vs. prior 90 days)
SUPPORT ANALYSIS:
- Open cases: 2 (1 high priority)
- Recent escalation: Yes (3 days ago)
- Sentiment: Slightly negative
OPPORTUNITY ANALYSIS:
- No new opportunities in 6 months
- Last deal: Won 8 months ago
RECOMMENDATION:
Schedule customer success call to address open cases and explore expansion opportunities.
Example 3: Renewal Risk Analysis
User says: "Show me accounts with renewals coming up that are at risk"
Actions:
- Filter accounts with renewals in next 90 days
- Cross-reference with risk signals
- Prioritize by revenue and risk score
Result:
AT-RISK RENEWALS (Next 90 Days):
1. Alpine Ski House - $150K ARR - Renews in 30 days
Risk: High engagement drop + unresolved escalation
2. Tailspin Toys - $80K ARR - Renews in 60 days
Risk: Budget freeze mentioned in recent call notes
Troubleshooting
Error: No risk data available
Cause: Sales Insights or Relationship Analytics not enabled Solution:
- Fall back to activity count analysis
- Use support case data as primary signal
- Calculate manual engagement score from activitypointer
Error: Too many false positives
Cause: Thresholds too sensitive for organization's engagement patterns Solution:
- Adjust activity threshold based on account tier
- Consider industry-specific baselines
- Weight signals based on historical churn correlation
Error: Missing support case data
Cause: Customer service not using Dynamics 365 for cases Solution:
- Focus on activity and opportunity signals
- Consider integrating external support data
- Use activity notes for sentiment analysis