CRM Retention Analytics Skill
Overview
Generate a retention action-board JSON for a filtered set of CRM accounts by querying a remote API, computing risk levels, expansion pipeline, and follow-up actions from account, opportunity, ticket, NPS, and metric data.
API Base URL
Read environment_access.md in the solver attempt directory to get GDPEVO_ENV_BASE_URL. Use that value as the API base URL. Do not hard-code localhost as the operative base URL.
Available Endpoints
| Endpoint |
Method |
Description |
/api/health |
GET |
Health check |
/api/accounts |
GET |
List all accounts (returns accounts array and count) |
/api/accounts/{account_id} |
GET |
Single account details |
/api/accounts/{account_id}/tickets |
GET |
Support tickets for the account |
/api/accounts/{account_id}/nps |
GET |
NPS survey responses for the account |
/api/accounts/{account_id}/metrics |
GET |
Monthly metrics for the account |
/api/opportunities |
GET |
All opportunities (returns opportunities array and count). Note: Query parameters such as account_id or state do not filter the response; filter client-side. |
Note: The task prompt may reference /api/v1/accounts/retention. That endpoint does not exist in this environment. Use the endpoints listed above.
Account Object Fields
account_id – stable identifier (e.g. acct_globex_north)
display_name – human-readable name
legal_name – full legal entity name
account_aliases – array of known aliases
segment – SMB | Mid-Market | Enterprise | Strategic
region – e.g. North America, EMEA, APAC, LATAM
lifecycle_status – active | implementation | renewal_risk | paused
renewal_date – ISO date string (YYYY-MM-DD)
contract_tenure_months – integer
product_plan – e.g. Launch, Growth, Scale, Enterprise, Strategic
csm_owner – assigned CSM name
billing_arr_current – float
crm_arr – float (usually the authoritative current_arr value for the board)
Opportunity Object Fields
opportunity_id
account_id
amount – float
state – open | closed | cancelled
stage – e.g. Discovery, Proposal
close_date
product_line
region
Ticket Object Fields
ticket_id
account_id
status – open | closed | cancelled
severity – P1 … P4
product_area
first_response_sla_met – boolean
resolution_sla_met – boolean
is_duplicate – boolean
is_spam – boolean
created_date
NPS Response Fields
response_id
account_id
response_date
score – integer (0-100)
Monthly Metric Fields
month – e.g. 2026-01
quarter – e.g. 2026-Q1
account_id
active_seats – integer
nps_score – integer or null
product_usage – float (percentage)
recognized_revenue – float
sla_compliance – float (percentage)
support_ticket_count – integer
survey_status – completed | missing
Workflow
- Read the task prompt to extract the filter criteria (region, segment, lifecycle status, renewal-date window, tenure threshold, etc.).
- Fetch
/api/accounts and apply the filter.
- Fetch
/api/opportunities and build an in-memory map of account_id → open opportunities.
- For each target account:
- Compute
expansion_pipeline = sum of amount for opportunities where state === 'open'.
- (Optional but recommended) Fetch
/api/accounts/{id}/tickets, /api/accounts/{id}/nps, and /api/accounts/{id}/metrics to enrich risk analysis.
- Determine risk level using this priority:
critical – lifecycle_status === 'renewal_risk'
high – lifecycle_status === 'paused'
medium – lifecycle_status === 'active' with at least one risk indicator (low NPS < 40, high open-ticket count ≥ 8, low usage < 60, SLA compliance < 90, or renewal within 60 days)
low – otherwise
- Determine primary action:
collections_followup – lifecycle_status === 'paused'
renewal_save – lifecycle_status === 'renewal_risk'
technical_recovery – active account with significant support backlog (≥ 8 open tickets or SLA compliance < 90)
executive_qbr – active account with large expansion pipeline (> 0) and no critical support issues
nurture_monitor – low-risk active accounts with no expansion pipeline
- Build
reason_codes based on the actual drivers:
renewal_risk – for lifecycle_status === 'renewal_risk'
account_paused – for lifecycle_status === 'paused'
support_backlog – for accounts with ≥ 8 open tickets
low_nps – for latest NPS < 45
low_usage – for latest product_usage < 60
expansion_opportunity – for active accounts with open expansion pipeline
renewal_approaching – default for active accounts
- Sort the action board:
- Primary: risk level ascending (
critical → high → medium → low)
- Secondary:
current_arr descending
- Assign sequential
rank starting at 1.
- Compute segment summary:
strategic_accounts – count of target accounts where segment === 'Strategic'
enterprise_accounts – count where segment === 'Enterprise'
arr_at_risk – sum of current_arr for all board rows
open_expansion_pipeline – sum of expansion_pipeline
net_revenue_exposure – arr_at_risk + open_expansion_pipeline
- Compute follow-up calendar:
- For each action type present on the board, set the calendar date to the earliest
next_touch_due_date of any board row with that primary_action.
- For action types not present, set a default date (e.g., today + 14 days).
- Select policy codes. The answer template shows option ranges separated by
|:
risk_model_code – RS-2 | RS-6 | RS-9
arr_source_code – REV-1 | REV-4 | REV-8
support_hygiene_code – SUP-3 | SUP-8 | SUP-9
action_priority_code – ACT-1 | ACT-5 | ACT-7
board_sort_code – BORD-1 | BORD-4 | BORD-8
exposure_formula_code – EXP-2 | EXP-6 | EXP-9
calendar_policy_code – CAL-3 | CAL-5 | CAL-7
- Choose one code from each pipe-separated list. Heuristic: if the board uses a simple 3-level risk model and CRM ARR, prefer the lowest-suffix option (
RS-2, REV-1, SUP-3, ACT-1, BORD-1, EXP-2, CAL-3).
Output Field Definitions
| Field |
Type |
Rule |
rank |
integer |
1-based after sorting |
account_id |
string |
Exact API account_id |
risk_level |
string |
critical | high | medium | low |
primary_action |
string |
collections_followup | technical_recovery | renewal_save | executive_qbr | nurture_monitor |
current_arr |
float |
Use crm_arr from account object (round to 2 decimals) |
expansion_pipeline |
float |
Sum of open-opportunity amounts (round to 2 decimals) |
overdue_balance |
float |
No invoices endpoint is available; default to 0.0 |
next_touch_due_date |
string YYYY-MM-DD |
Based on risk: critical = +1 day, high = +3 days, medium = +7 days, low = +14 days from today |
reason_codes |
string[] |
Non-empty array of driver codes |
Pitfalls
- Do not assume
/api/v1/accounts/retention exists. The prompt may reference it, but the actual usable endpoints are /api/accounts, /api/opportunities, and the account sub-endpoints (/tickets, /nps, /metrics).
- Opportunity filtering is client-side. The
/api/opportunities endpoint returns all 114 opportunities regardless of query params. Always filter by state === 'open' and account_id in memory.
- No billing/invoice endpoint.
overdue_balance cannot be computed from available data; default it to 0.0.
- Floating-point precision. Round monetary sums to 2 decimal places before serializing to JSON to avoid
4987847.749999999 artifacts.
- Date context. Use the current date from the task context (e.g.,
2026-07-02) when computing next_touch_due_date and follow-up calendar dates.
- Policy codes. Each policy code field accepts exactly one value from its pipe-separated option list; do not concatenate multiple options with
|.
1---2name: reflect-3-attempt-03-333description: CRM Retention Analytics Skill4---5# CRM Retention Analytics Skill67## Overview89Generate a retention action-board JSON for a filtered set of CRM accounts by querying a remote API, computing risk levels, expansion pipeline, and follow-up actions from account, opportunity, ticket, NPS, and metric data.1011## API Base URL1213Read `environment_access.md` in the solver attempt directory to get `GDPEVO_ENV_BASE_URL`. Use that value as the API base URL. Do **not** hard-code `localhost` as the operative base URL.1415## Available Endpoints1617| Endpoint | Method | Description |18|----------|--------|-------------|19| `/api/health` | GET | Health check |20| `/api/accounts` | GET | List all accounts (returns `accounts` array and `count`) |21| `/api/accounts/{account_id}` | GET | Single account details |22| `/api/accounts/{account_id}/tickets` | GET | Support tickets for the account |23| `/api/accounts/{account_id}/nps` | GET | NPS survey responses for the account |24| `/api/accounts/{account_id}/metrics` | GET | Monthly metrics for the account |25| `/api/opportunities` | GET | All opportunities (returns `opportunities` array and `count`). **Note:** Query parameters such as `account_id` or `state` do **not** filter the response; filter client-side. |2627> **Note:** The task prompt may reference `/api/v1/accounts/retention`. That endpoint does **not** exist in this environment. Use the endpoints listed above.2829## Account Object Fields3031- `account_id` – stable identifier (e.g. `acct_globex_north`)32- `display_name` – human-readable name33- `legal_name` – full legal entity name34- `account_aliases` – array of known aliases35- `segment` – `SMB` | `Mid-Market` | `Enterprise` | `Strategic`36- `region` – e.g. `North America`, `EMEA`, `APAC`, `LATAM`37- `lifecycle_status` – `active` | `implementation` | `renewal_risk` | `paused`38- `renewal_date` – ISO date string (`YYYY-MM-DD`)39- `contract_tenure_months` – integer40- `product_plan` – e.g. `Launch`, `Growth`, `Scale`, `Enterprise`, `Strategic`41- `csm_owner` – assigned CSM name42- `billing_arr_current` – float43- `crm_arr` – float (usually the authoritative `current_arr` value for the board)4445## Opportunity Object Fields4647- `opportunity_id`48- `account_id`49- `amount` – float50- `state` – `open` | `closed` | `cancelled`51- `stage` – e.g. `Discovery`, `Proposal`52- `close_date`53- `product_line`54- `region`5556## Ticket Object Fields5758- `ticket_id`59- `account_id`60- `status` – `open` | `closed` | `cancelled`61- `severity` – `P1` … `P4`62- `product_area`63- `first_response_sla_met` – boolean64- `resolution_sla_met` – boolean65- `is_duplicate` – boolean66- `is_spam` – boolean67- `created_date`6869## NPS Response Fields7071- `response_id`72- `account_id`73- `response_date`74- `score` – integer (0-100)7576## Monthly Metric Fields7778- `month` – e.g. `2026-01`79- `quarter` – e.g. `2026-Q1`80- `account_id`81- `active_seats` – integer82- `nps_score` – integer or `null`83- `product_usage` – float (percentage)84- `recognized_revenue` – float85- `sla_compliance` – float (percentage)86- `support_ticket_count` – integer87- `survey_status` – `completed` | `missing`8889## Workflow90911. **Read the task prompt** to extract the filter criteria (region, segment, lifecycle status, renewal-date window, tenure threshold, etc.).922. **Fetch** `/api/accounts` and apply the filter.933. **Fetch** `/api/opportunities` and build an in-memory map of `account_id → open opportunities`.944. For **each** target account:95 - Compute `expansion_pipeline` = sum of `amount` for opportunities where `state === 'open'`.96 - (Optional but recommended) Fetch `/api/accounts/{id}/tickets`, `/api/accounts/{id}/nps`, and `/api/accounts/{id}/metrics` to enrich risk analysis.975. **Determine risk level** using this priority:98 - `critical` – `lifecycle_status === 'renewal_risk'`99 - `high` – `lifecycle_status === 'paused'`100 - `medium` – `lifecycle_status === 'active'` with at least one risk indicator (low NPS < 40, high open-ticket count ≥ 8, low usage < 60, SLA compliance < 90, or renewal within 60 days)101 - `low` – otherwise1026. **Determine primary action**:103 - `collections_followup` – `lifecycle_status === 'paused'`104 - `renewal_save` – `lifecycle_status === 'renewal_risk'`105 - `technical_recovery` – active account with significant support backlog (≥ 8 open tickets or SLA compliance < 90)106 - `executive_qbr` – active account with large expansion pipeline (> 0) and no critical support issues107 - `nurture_monitor` – low-risk active accounts with no expansion pipeline1087. **Build `reason_codes`** based on the actual drivers:109 - `renewal_risk` – for `lifecycle_status === 'renewal_risk'`110 - `account_paused` – for `lifecycle_status === 'paused'`111 - `support_backlog` – for accounts with ≥ 8 open tickets112 - `low_nps` – for latest NPS < 45113 - `low_usage` – for latest `product_usage` < 60114 - `expansion_opportunity` – for active accounts with open expansion pipeline115 - `renewal_approaching` – default for active accounts1168. **Sort the action board**:117 - Primary: risk level ascending (`critical` → `high` → `medium` → `low`)118 - Secondary: `current_arr` descending119 - Assign sequential `rank` starting at 1.1209. **Compute segment summary**:121 - `strategic_accounts` – count of target accounts where `segment === 'Strategic'`122 - `enterprise_accounts` – count where `segment === 'Enterprise'`123 - `arr_at_risk` – sum of `current_arr` for all board rows124 - `open_expansion_pipeline` – sum of `expansion_pipeline`125 - `net_revenue_exposure` – `arr_at_risk + open_expansion_pipeline`12610. **Compute follow-up calendar**:127 - For each action type present on the board, set the calendar date to the *earliest* `next_touch_due_date` of any board row with that `primary_action`.128 - For action types not present, set a default date (e.g., today + 14 days).12911. **Select policy codes**. The answer template shows option ranges separated by `|`:130 - `risk_model_code` – `RS-2` | `RS-6` | `RS-9`131 - `arr_source_code` – `REV-1` | `REV-4` | `REV-8`132 - `support_hygiene_code` – `SUP-3` | `SUP-8` | `SUP-9`133 - `action_priority_code` – `ACT-1` | `ACT-5` | `ACT-7`134 - `board_sort_code` – `BORD-1` | `BORD-4` | `BORD-8`135 - `exposure_formula_code` – `EXP-2` | `EXP-6` | `EXP-9`136 - `calendar_policy_code` – `CAL-3` | `CAL-5` | `CAL-7`137 - Choose **one** code from each pipe-separated list. Heuristic: if the board uses a simple 3-level risk model and CRM ARR, prefer the lowest-suffix option (`RS-2`, `REV-1`, `SUP-3`, `ACT-1`, `BORD-1`, `EXP-2`, `CAL-3`).138139## Output Field Definitions140141| Field | Type | Rule |142|-------|------|------|143| `rank` | integer | 1-based after sorting |144| `account_id` | string | Exact API `account_id` |145| `risk_level` | string | `critical` \| `high` \| `medium` \| `low` |146| `primary_action` | string | `collections_followup` \| `technical_recovery` \| `renewal_save` \| `executive_qbr` \| `nurture_monitor` |147| `current_arr` | float | Use `crm_arr` from account object (round to 2 decimals) |148| `expansion_pipeline` | float | Sum of open-opportunity amounts (round to 2 decimals) |149| `overdue_balance` | float | No invoices endpoint is available; default to `0.0` |150| `next_touch_due_date` | string `YYYY-MM-DD` | Based on risk: critical = +1 day, high = +3 days, medium = +7 days, low = +14 days from today |151| `reason_codes` | string[] | Non-empty array of driver codes |152153## Pitfalls154155- **Do not assume `/api/v1/accounts/retention` exists.** The prompt may reference it, but the actual usable endpoints are `/api/accounts`, `/api/opportunities`, and the account sub-endpoints (`/tickets`, `/nps`, `/metrics`).156- **Opportunity filtering is client-side.** The `/api/opportunities` endpoint returns all 114 opportunities regardless of query params. Always filter by `state === 'open'` and `account_id` in memory.157- **No billing/invoice endpoint.** `overdue_balance` cannot be computed from available data; default it to `0.0`.158- **Floating-point precision.** Round monetary sums to 2 decimal places before serializing to JSON to avoid `4987847.749999999` artifacts.159- **Date context.** Use the current date from the task context (e.g., `2026-07-02`) when computing `next_touch_due_date` and follow-up calendar dates.160- **Policy codes.** Each policy code field accepts exactly one value from its pipe-separated option list; do not concatenate multiple options with `|`.