CRM Retention Analytics Skill
API Base URL
Use the API base URL from the solver's environment: read environment_access.md for the operative GDPEVO_ENV_BASE_URL. Do not hard-code localhost or 127.0.0.1 as the base URL.
Endpoint Inventory
| Endpoint |
Method |
Description |
/api/health |
GET |
Service health, seed, and row counts for all underlying data sources. Use to verify connectivity and discover dataset sizes. |
/api/accounts |
GET |
All accounts. Returns { "accounts": [...], "count": N }. No query-parameter filtering is supported; filter client-side. |
/api/accounts/{account_id} |
GET |
Single account record with full profile. |
/api/accounts/{account_id}/metrics |
GET |
Monthly account metrics (12 records). Returns { "account_id", "count", "metrics": [...] }. |
/api/accounts/{account_id}/nps |
GET |
NPS response history. Returns { "account_id", "count", "nps_responses": [...] }. |
/api/accounts/{account_id}/tickets |
GET |
Support ticket history. Returns { "account_id", "count", "tickets": [...] }. |
/api/opportunities |
GET |
All CRM opportunities. Returns { "opportunities": [...], "count": N }. No query-parameter filtering is supported; filter client-side. |
Note on undiscovered endpoints: The health endpoint references additional datasets (ar_aging.json, billing_snapshots.json, churn_candidates.csv, event_performance.json, hr_summary.json) that may not be exposed as public REST endpoints in all environments. If a task requires receivables, HR headcount, event orders, or churn-risk scores and no dedicated endpoint responds, derive the values from the available endpoints or request guidance. Always attempt the standard /api/* path first, then account-scoped /api/accounts/{id}/<resource> variants.
Data Models
Account
account_id (string, e.g. acct_globex_north)
account_aliases (string[])
display_name (string)
legal_name (string)
segment (string enum: Strategic, Enterprise, Mid-Market)
product_plan (string enum: Launch, Growth, Scale, Enterprise, Strategic)
lifecycle_status (string enum: active, implementation, renewal_risk, paused)
region (string)
csm_owner (string)
contract_tenure_months (integer)
renewal_date (string, YYYY-MM-DD)
crm_arr (float) — ARR as recorded in CRM
billing_arr_current (float) — ARR as recorded in billing
Opportunity
opportunity_id (string)
account_id (string)
account_legal_name (string)
product_line (string enum: AI Assist, Workflow Plus, Core Retention, Data Cloud)
stage (string enum: Discovery, Prospecting, Proposal, Negotiation, Closed Won, Closed Lost)
state (string enum: open, closed) — open = not yet won or lost; closed = won or lost
amount (float)
close_date (string, YYYY-MM-DD)
created_date (string, YYYY-MM-DD)
region (string)
Monthly Metric
month (string, YYYY-MM)
quarter (string, YYYY-QN)
active_seats (integer)
product_usage (float, 0–100)
nps_score (integer)
recognized_revenue (float)
sla_compliance (float, 0–100)
support_ticket_count (integer)
survey_status (string enum: completed, pending, skipped)
NPS Response
response_id (string)
response_date (string, YYYY-MM-DD)
score (integer, 0–100)
survey_channel (string enum: email, in_app, csm_call)
retracted (boolean)
Support Ticket
ticket_id (string)
created_date (string, YYYY-MM-DD)
status (string enum: open, closed)
severity (string enum: P1, P2, P3, P4)
product_area (string)
first_response_sla_met (boolean)
resolution_sla_met (boolean)
is_spam (boolean)
is_duplicate (boolean)
Business Rules & Filtering Logic
Quarter Scoping
- "Current quarter" or "this quarter" in task text refers to the quarter of the most recent metric month returned by
/api/accounts/{id}/metrics. Derive it from the quarter field (e.g., 2026-Q2).
- When a task asks for pipeline or metrics "for this quarter", filter opportunities or metrics whose
quarter matches the current quarter, or whose close_date / month falls within that quarter's date range.
Retention Pipeline (High-Risk Accounts)
- Criteria typically include:
crm_arr or billing_arr_current > threshold (e.g., 5000)
- Churn risk > threshold (e.g., 0.7) — when churn-risk scores are unavailable, proxy with:
lifecycle_status == "renewal_risk"
- Low recent NPS (
nps_score below ~40)
- Declining
product_usage over the last 3 months
- High open-ticket volume with poor SLA compliance
- Segment filters:
Strategic and Enterprise are the top two tiers; include both when the task asks for "strategic and enterprise accounts".
Pipeline Summary
won_count: Opportunities with state == "closed" and stage == "Closed Won"
won_revenue: Sum of amount for won opportunities
lost_count: Opportunities with state == "closed" and stage == "Closed Lost"
open_count: Opportunities with state == "open"
open_pipeline: Sum of amount for open opportunities
win_rate_pct: won_count / (won_count + lost_count) * 100.0 — compute as a float with one decimal place
top_open_product_line: The product_line with the highest total open amount among open opportunities; break ties alphabetically
Support Health (Proxy)
- When "low support health" is requested but no composite health score endpoint exists, derive from:
sla_compliance in the latest month (lower = worse)
- Ratio of
resolution_sla_met == false tickets to total tickets
- High
support_ticket_count in recent metrics
- Very low NPS scores
ARR Source Selection
- Prefer
crm_arr for CRM-facing reports (retention pipeline, expansion potential).
- Use
billing_arr_current when the task explicitly asks for billing/receivables context.
- If only one is needed, document which was used in reasoning; default to
crm_arr for retention analytics.
Link Status (Overdue Follow-ups)
linked: An overdue receivable record has a matching account_id in the CRM accounts list.
unlinked: An overdue receivable record has no matching account_id in the CRM accounts list.
- When receivables data is unavailable from a dedicated endpoint, use
billing_arr_current discrepancies or negative indicators from billing snapshots as proxies, and clearly note the derivation.
Risk Level & Ranking
critical: Account meets multiple risk criteria (e.g., overdue receivables + renewal risk status + low support health)
high: Meets two major criteria
medium: Meets one criterion
low: Meets none
- Rank the action board by severity (critical first), then by
current_arr descending, then by overdue_balance descending.
Primary Actions
collections_followup: Account has overdue receivables
technical_recovery: Low support health / high unresolved ticket volume
renewal_save: lifecycle_status == "renewal_risk" or churn risk high
executive_qbr: Strategic account with expansion pipeline but no immediate risk
nurture_monitor: Active account with no immediate risk
Reason Codes
overdue_receivable
high_churn_risk
low_support_health
renewal_at_risk
declining_usage
Follow-up Calendar
- Compute due dates relative to the current date (or the latest metric month if the current date is not available):
collections_followup: Current date + 1 business day
technical_recovery: Current date + 2 business days
renewal_save: Current date + 3 business days
executive_qbr: Current date + 5 business days
nurture_monitor: Current date + 7 business days
- Format as
YYYY-MM-DD.
Output Schemas
There are two primary output shapes used across tasks. Inspect the task's answer template to determine which is required.
Shape A — Pipeline & Financial Summary
{
"financial_summary": {
"overdue_client_count": 0,
"overdue_total": 0.00,
"linked_followup_count": 0,
"unlinked_followup_count": 0
},
"pipeline_summary": {
"won_count": 0,
"won_revenue": 0.00,
"lost_count": 0,
"open_count": 0,
"open_pipeline": 0.00,
"win_rate_pct": 0.0,
"top_open_product_line": ""
},
"overdue_followups": [
{
"customer_name": "",
"link_status": "linked",
"account_id": null,
"overdue_balance": 0.00,
"due_date": "YYYY-MM-DD",
"primary_action": "collections_followup"
}
],
"ops_context": {
"hr_headcount": 0,
"unpaid_claims_total": 0.00,
"event_orders": 0,
"event_revenue": 0.00
},
"policy_codes": {
"receivable_trigger_code": "RCP-4|RCP-7|RCP-9",
"crm_match_code": "CM-2|CM-5|CM-8",
"pipeline_window_code": "PW-3|PW-6|PW-9",
"followup_scope_code": "FS-1|FS-4|FS-8"
}
}
Shape B — Executive Action Board
{
"action_board": [
{
"rank": 1,
"account_id": "acct_example",
"risk_level": "critical",
"primary_action": "collections_followup",
"current_arr": 0.0,
"expansion_pipeline": 0.0,
"overdue_balance": 0.0,
"next_touch_due_date": "YYYY-MM-DD",
"reason_codes": ["overdue_receivable"]
}
],
"segment_summary": {
"strategic_accounts": 0,
"enterprise_accounts": 0,
"arr_at_risk": 0.0,
"open_expansion_pipeline": 0.0,
"net_revenue_exposure": 0.0
},
"followup_calendar": {
"collections_followup": "YYYY-MM-DD",
"technical_recovery": "YYYY-MM-DD",
"renewal_save": "YYYY-MM-DD",
"executive_qbr": "YYYY-MM-DD",
"nurture_monitor": "YYYY-MM-DD"
},
"policy_codes": {
"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"
}
}
Policy Codes
policy_codes is a required top-level object in both shapes.
- Populate it with the pipe-delimited code strings shown in the answer template (e.g.,
"RCP-4|RCP-7|RCP-9"). Do not modify the codes unless the task explicitly asks for a different policy configuration.
- These codes are metadata that indicate which business-rule variants were applied.
Workflow Rules
- Start with health check. Call
/api/health to confirm the API is up and note dataset sizes.
- Fetch all accounts and opportunities. These are the two global list endpoints. Fetch them first because most downstream logic joins against them.
- Fetch account-scoped data on demand. For accounts that pass initial filters, call
/api/accounts/{id}/metrics, /api/accounts/{id}/nps, and /api/accounts/{id}/tickets as needed. Do not fetch every account's detail data if the task only asks for a small subset.
- Filter client-side. The API does not support query-parameter filtering. Apply all filters (segment, ARR threshold, lifecycle status, quarter, etc.) in your code after fetching the data.
- Derive missing fields explicitly. If a required field (e.g.,
overdue_balance, churn_risk) has no direct endpoint, derive it from available data and document the derivation in the output reasoning.
- Match accounts carefully. Join on
account_id (not display_name or legal_name) because aliases vary. Use account_legal_name from opportunities or display_name from accounts only for human-readable labels.
- Use
crm_arr as the canonical ARR unless the task specifies billing ARR.
- Format monetary values to two decimal places (
0.00) and percentages to one decimal place (0.0).
- Sort consistently. For action boards, sort by risk level severity, then
current_arr desc, then overdue_balance desc. For follow-up lists, sort by due_date asc, then overdue_balance desc.
- Respect nulls. If an account has no opportunities, its
expansion_pipeline is 0.0. If no receivables data exists, overdue_balance is 0.0 and overdue_client_count is 0.
Pitfalls
- Do not assume query parameters work.
/api/accounts?segment=Enterprise still returns all accounts; filter in code.
- Do not hard-code localhost. Always read the base URL from
environment_access.md / GDPEVO_ENV_BASE_URL.
- Do not confuse
crm_arr with billing_arr_current. They can differ significantly (e.g., Globex North: 1,057,320 vs 1,188,000).
- Do not ignore
lifecycle_status. It is the strongest available proxy for churn risk when no dedicated churn-risk endpoint exists.
- Do not include retracted NPS scores in health calculations unless the task explicitly asks for them. Filter
retracted == false.
- Do not include spam or duplicate tickets in support-health calculations unless instructed otherwise.
- Watch for missing endpoints. If a task requires data from
ar_aging, hr_summary, or event_performance and no /api/* endpoint returns it, derive from the closest available proxy or return 0 values with clear documentation.
- Policy codes must match the template exactly. Do not invent new codes or omit the object.
- Date formats must be
YYYY-MM-DD. No timezone offsets, no timestamps.
1---2name: self-attempt-01-313description: CRM Retention Analytics Skill4---5# CRM Retention Analytics Skill67## API Base URL89Use the API base URL from the solver's environment: read `environment_access.md` for the operative `GDPEVO_ENV_BASE_URL`. Do not hard-code `localhost` or `127.0.0.1` as the base URL.1011## Endpoint Inventory1213| Endpoint | Method | Description |14|----------|--------|-------------|15| `/api/health` | GET | Service health, seed, and row counts for all underlying data sources. Use to verify connectivity and discover dataset sizes. |16| `/api/accounts` | GET | All accounts. Returns `{ "accounts": [...], "count": N }`. No query-parameter filtering is supported; filter client-side. |17| `/api/accounts/{account_id}` | GET | Single account record with full profile. |18| `/api/accounts/{account_id}/metrics` | GET | Monthly account metrics (12 records). Returns `{ "account_id", "count", "metrics": [...] }`. |19| `/api/accounts/{account_id}/nps` | GET | NPS response history. Returns `{ "account_id", "count", "nps_responses": [...] }`. |20| `/api/accounts/{account_id}/tickets` | GET | Support ticket history. Returns `{ "account_id", "count", "tickets": [...] }`. |21| `/api/opportunities` | GET | All CRM opportunities. Returns `{ "opportunities": [...], "count": N }`. No query-parameter filtering is supported; filter client-side. |2223**Note on undiscovered endpoints:** The health endpoint references additional datasets (`ar_aging.json`, `billing_snapshots.json`, `churn_candidates.csv`, `event_performance.json`, `hr_summary.json`) that may not be exposed as public REST endpoints in all environments. If a task requires receivables, HR headcount, event orders, or churn-risk scores and no dedicated endpoint responds, derive the values from the available endpoints or request guidance. Always attempt the standard `/api/*` path first, then account-scoped `/api/accounts/{id}/<resource>` variants.2425## Data Models2627### Account28- `account_id` (string, e.g. `acct_globex_north`)29- `account_aliases` (string[])30- `display_name` (string)31- `legal_name` (string)32- `segment` (string enum: `Strategic`, `Enterprise`, `Mid-Market`)33- `product_plan` (string enum: `Launch`, `Growth`, `Scale`, `Enterprise`, `Strategic`)34- `lifecycle_status` (string enum: `active`, `implementation`, `renewal_risk`, `paused`)35- `region` (string)36- `csm_owner` (string)37- `contract_tenure_months` (integer)38- `renewal_date` (string, `YYYY-MM-DD`)39- `crm_arr` (float) — ARR as recorded in CRM40- `billing_arr_current` (float) — ARR as recorded in billing4142### Opportunity43- `opportunity_id` (string)44- `account_id` (string)45- `account_legal_name` (string)46- `product_line` (string enum: `AI Assist`, `Workflow Plus`, `Core Retention`, `Data Cloud`)47- `stage` (string enum: `Discovery`, `Prospecting`, `Proposal`, `Negotiation`, `Closed Won`, `Closed Lost`)48- `state` (string enum: `open`, `closed`) — `open` = not yet won or lost; `closed` = won or lost49- `amount` (float)50- `close_date` (string, `YYYY-MM-DD`)51- `created_date` (string, `YYYY-MM-DD`)52- `region` (string)5354### Monthly Metric55- `month` (string, `YYYY-MM`)56- `quarter` (string, `YYYY-QN`)57- `active_seats` (integer)58- `product_usage` (float, 0–100)59- `nps_score` (integer)60- `recognized_revenue` (float)61- `sla_compliance` (float, 0–100)62- `support_ticket_count` (integer)63- `survey_status` (string enum: `completed`, `pending`, `skipped`)6465### NPS Response66- `response_id` (string)67- `response_date` (string, `YYYY-MM-DD`)68- `score` (integer, 0–100)69- `survey_channel` (string enum: `email`, `in_app`, `csm_call`)70- `retracted` (boolean)7172### Support Ticket73- `ticket_id` (string)74- `created_date` (string, `YYYY-MM-DD`)75- `status` (string enum: `open`, `closed`)76- `severity` (string enum: `P1`, `P2`, `P3`, `P4`)77- `product_area` (string)78- `first_response_sla_met` (boolean)79- `resolution_sla_met` (boolean)80- `is_spam` (boolean)81- `is_duplicate` (boolean)8283## Business Rules & Filtering Logic8485### Quarter Scoping86- "Current quarter" or "this quarter" in task text refers to the quarter of the most recent metric month returned by `/api/accounts/{id}/metrics`. Derive it from the `quarter` field (e.g., `2026-Q2`).87- When a task asks for pipeline or metrics "for this quarter", filter opportunities or metrics whose `quarter` matches the current quarter, or whose `close_date` / `month` falls within that quarter's date range.8889### Retention Pipeline (High-Risk Accounts)90- Criteria typically include:91 - `crm_arr` or `billing_arr_current` > threshold (e.g., 5000)92 - Churn risk > threshold (e.g., 0.7) — when churn-risk scores are unavailable, proxy with:93 - `lifecycle_status == "renewal_risk"`94 - Low recent NPS (`nps_score` below ~40)95 - Declining `product_usage` over the last 3 months96 - High open-ticket volume with poor SLA compliance97- Segment filters: `Strategic` and `Enterprise` are the top two tiers; include both when the task asks for "strategic and enterprise accounts".9899### Pipeline Summary100- `won_count`: Opportunities with `state == "closed"` and `stage == "Closed Won"`101- `won_revenue`: Sum of `amount` for won opportunities102- `lost_count`: Opportunities with `state == "closed"` and `stage == "Closed Lost"`103- `open_count`: Opportunities with `state == "open"`104- `open_pipeline`: Sum of `amount` for open opportunities105- `win_rate_pct`: `won_count / (won_count + lost_count) * 100.0` — compute as a float with one decimal place106- `top_open_product_line`: The `product_line` with the highest total open `amount` among open opportunities; break ties alphabetically107108### Support Health (Proxy)109- When "low support health" is requested but no composite health score endpoint exists, derive from:110 - `sla_compliance` in the latest month (lower = worse)111 - Ratio of `resolution_sla_met == false` tickets to total tickets112 - High `support_ticket_count` in recent metrics113 - Very low NPS scores114115### ARR Source Selection116- Prefer `crm_arr` for CRM-facing reports (retention pipeline, expansion potential).117- Use `billing_arr_current` when the task explicitly asks for billing/receivables context.118- If only one is needed, document which was used in reasoning; default to `crm_arr` for retention analytics.119120### Link Status (Overdue Follow-ups)121- `linked`: An overdue receivable record has a matching `account_id` in the CRM accounts list.122- `unlinked`: An overdue receivable record has no matching `account_id` in the CRM accounts list.123- When receivables data is unavailable from a dedicated endpoint, use `billing_arr_current` discrepancies or negative indicators from billing snapshots as proxies, and clearly note the derivation.124125### Risk Level & Ranking126- `critical`: Account meets multiple risk criteria (e.g., overdue receivables + renewal risk status + low support health)127- `high`: Meets two major criteria128- `medium`: Meets one criterion129- `low`: Meets none130- Rank the action board by severity (critical first), then by `current_arr` descending, then by `overdue_balance` descending.131132### Primary Actions133- `collections_followup`: Account has overdue receivables134- `technical_recovery`: Low support health / high unresolved ticket volume135- `renewal_save`: `lifecycle_status == "renewal_risk"` or churn risk high136- `executive_qbr`: Strategic account with expansion pipeline but no immediate risk137- `nurture_monitor`: Active account with no immediate risk138139### Reason Codes140- `overdue_receivable`141- `high_churn_risk`142- `low_support_health`143- `renewal_at_risk`144- `declining_usage`145146### Follow-up Calendar147- Compute due dates relative to the current date (or the latest metric month if the current date is not available):148 - `collections_followup`: Current date + 1 business day149 - `technical_recovery`: Current date + 2 business days150 - `renewal_save`: Current date + 3 business days151 - `executive_qbr`: Current date + 5 business days152 - `nurture_monitor`: Current date + 7 business days153- Format as `YYYY-MM-DD`.154155## Output Schemas156157There are two primary output shapes used across tasks. Inspect the task's answer template to determine which is required.158159### Shape A — Pipeline & Financial Summary160```json161{162 "financial_summary": {163 "overdue_client_count": 0,164 "overdue_total": 0.00,165 "linked_followup_count": 0,166 "unlinked_followup_count": 0167 },168 "pipeline_summary": {169 "won_count": 0,170 "won_revenue": 0.00,171 "lost_count": 0,172 "open_count": 0,173 "open_pipeline": 0.00,174 "win_rate_pct": 0.0,175 "top_open_product_line": ""176 },177 "overdue_followups": [178 {179 "customer_name": "",180 "link_status": "linked",181 "account_id": null,182 "overdue_balance": 0.00,183 "due_date": "YYYY-MM-DD",184 "primary_action": "collections_followup"185 }186 ],187 "ops_context": {188 "hr_headcount": 0,189 "unpaid_claims_total": 0.00,190 "event_orders": 0,191 "event_revenue": 0.00192 },193 "policy_codes": {194 "receivable_trigger_code": "RCP-4|RCP-7|RCP-9",195 "crm_match_code": "CM-2|CM-5|CM-8",196 "pipeline_window_code": "PW-3|PW-6|PW-9",197 "followup_scope_code": "FS-1|FS-4|FS-8"198 }199}200```201202### Shape B — Executive Action Board203```json204{205 "action_board": [206 {207 "rank": 1,208 "account_id": "acct_example",209 "risk_level": "critical",210 "primary_action": "collections_followup",211 "current_arr": 0.0,212 "expansion_pipeline": 0.0,213 "overdue_balance": 0.0,214 "next_touch_due_date": "YYYY-MM-DD",215 "reason_codes": ["overdue_receivable"]216 }217 ],218 "segment_summary": {219 "strategic_accounts": 0,220 "enterprise_accounts": 0,221 "arr_at_risk": 0.0,222 "open_expansion_pipeline": 0.0,223 "net_revenue_exposure": 0.0224 },225 "followup_calendar": {226 "collections_followup": "YYYY-MM-DD",227 "technical_recovery": "YYYY-MM-DD",228 "renewal_save": "YYYY-MM-DD",229 "executive_qbr": "YYYY-MM-DD",230 "nurture_monitor": "YYYY-MM-DD"231 },232 "policy_codes": {233 "risk_model_code": "RS-2|RS-6|RS-9",234 "arr_source_code": "REV-1|REV-4|REV-8",235 "support_hygiene_code": "SUP-3|SUP-8|SUP-9",236 "action_priority_code": "ACT-1|ACT-5|ACT-7",237 "board_sort_code": "BORD-1|BORD-4|BORD-8",238 "exposure_formula_code": "EXP-2|EXP-6|EXP-9",239 "calendar_policy_code": "CAL-3|CAL-5|CAL-7"240 }241}242```243244### Policy Codes245- `policy_codes` is a required top-level object in both shapes.246- Populate it with the pipe-delimited code strings shown in the answer template (e.g., `"RCP-4|RCP-7|RCP-9"`). Do not modify the codes unless the task explicitly asks for a different policy configuration.247- These codes are metadata that indicate which business-rule variants were applied.248249## Workflow Rules2502511. **Start with health check.** Call `/api/health` to confirm the API is up and note dataset sizes.2522. **Fetch all accounts and opportunities.** These are the two global list endpoints. Fetch them first because most downstream logic joins against them.2533. **Fetch account-scoped data on demand.** For accounts that pass initial filters, call `/api/accounts/{id}/metrics`, `/api/accounts/{id}/nps`, and `/api/accounts/{id}/tickets` as needed. Do not fetch every account's detail data if the task only asks for a small subset.2544. **Filter client-side.** The API does not support query-parameter filtering. Apply all filters (segment, ARR threshold, lifecycle status, quarter, etc.) in your code after fetching the data.2555. **Derive missing fields explicitly.** If a required field (e.g., `overdue_balance`, `churn_risk`) has no direct endpoint, derive it from available data and document the derivation in the output reasoning.2566. **Match accounts carefully.** Join on `account_id` (not `display_name` or `legal_name`) because aliases vary. Use `account_legal_name` from opportunities or `display_name` from accounts only for human-readable labels.2577. **Use `crm_arr` as the canonical ARR** unless the task specifies billing ARR.2588. **Format monetary values** to two decimal places (`0.00`) and percentages to one decimal place (`0.0`).2599. **Sort consistently.** For action boards, sort by risk level severity, then `current_arr` desc, then `overdue_balance` desc. For follow-up lists, sort by `due_date` asc, then `overdue_balance` desc.26010. **Respect nulls.** If an account has no opportunities, its `expansion_pipeline` is `0.0`. If no receivables data exists, `overdue_balance` is `0.0` and `overdue_client_count` is `0`.261262## Pitfalls263264- **Do not assume query parameters work.** `/api/accounts?segment=Enterprise` still returns all accounts; filter in code.265- **Do not hard-code localhost.** Always read the base URL from `environment_access.md` / `GDPEVO_ENV_BASE_URL`.266- **Do not confuse `crm_arr` with `billing_arr_current`.** They can differ significantly (e.g., Globex North: 1,057,320 vs 1,188,000).267- **Do not ignore `lifecycle_status`.** It is the strongest available proxy for churn risk when no dedicated churn-risk endpoint exists.268- **Do not include retracted NPS scores** in health calculations unless the task explicitly asks for them. Filter `retracted == false`.269- **Do not include spam or duplicate tickets** in support-health calculations unless instructed otherwise.270- **Watch for missing endpoints.** If a task requires data from `ar_aging`, `hr_summary`, or `event_performance` and no `/api/*` endpoint returns it, derive from the closest available proxy or return `0` values with clear documentation.271- **Policy codes must match the template exactly.** Do not invent new codes or omit the object.272- **Date formats must be `YYYY-MM-DD`.** No timezone offsets, no timestamps.