ApexCloud Retention Analytics — Reflect-3 Skill
Reusable rules for solving ApexCloud CRM retention-analytics tasks against the
ApexCloud Retention Operations data API. Read the data API base URL from the
task prompt (the http://127.0.0.1:8074 host in prompts is the documented
endpoint family; resolve to the live base URL given in the environment). These
rules are data/precision/labeling conventions — NOT gold answers and NOT a
test-time judge call.
0. Data API endpoints
/api/health→ row counts (use to sanity-check datasets: accounts=44, account_metrics=528, billing_snapshots=176, ar_aging=196 (4 quarters × 49), opportunities=114, hr_summary=16 (4 quarters × 4 regions), event_performance=20, nps_responses=451, support_tickets=1595, churn_train=180, churn_validation=60, churn_candidates=44)./api/accounts,/api/accounts/<id>/api/accounts/<id>/metrics?start=YYYY-MM&end=YYYY-MM(monthly: recognized_revenue, product_usage, sla_compliance, support_ticket_count, nps_score, active_seats, survey_status)/api/accounts/<id>/tickets?start=YYYY-MM-DD&end=YYYY-MM-DD(is_spam, is_duplicate, status: closed|cancelled, first_response_sla_met, resolution_sla_met)/api/accounts/<id>/nps?start=...&end=...(score, response_date, retracted)/api/billing/snapshots(account_id, as_of, billing_arr, mrr, legal_name, posted, source)/api/finance/ar-aging?as_of=YYYY-MM-DD(customer_name, 1_30, 31_60, 61_90, 90_plus, current, region, quarter)/api/opportunities?start=YYYY-MM-DD&end=YYYY-MM-DD[®ion=](account_id, account_legal_name, amount, close_date, stage: Discovery|Prospecting|Proposal|Negotiation|Closed Won|Closed Lost, state: open|closed, product_line)/api/hr/summary?quarter=YYYY-QN[®ion=]— OMIT region to get all regions (the literalregion=allreturns empty; sum headcount/unpaid_claims across the 4 region rows)./api/events/performance?event=apex_connect&quarter=YYYY-QN/exports/churn/train.csv,/exports/churn/validation.csv,/exports/churn/candidates.csv
1. UNIVERSAL CONVENTIONS (verified across train tasks)
ARR source — billing snapshot as_of the assessment date
current_arr= thebilling_arrfrom/api/billing/snapshotswith the largestas_of≤ the task's assessment/as-of date. Useposted=trueonly is not required (all are posted).- Do NOT use
account.billing_arr_current(that equals the year-end / 2026-12-31 snapshot) and do NOT useaccount.crm_arr. - Verification: train_005 score fell 0.190→0.095 when current_arr was switched
to
billing_arr_current; the snapshot value madearr_at_riskandnet_revenue_exposurecorrect.
Support tickets — clean count
clean_ticket_count/support_ticketsper period = tickets whereis_spam=falseANDis_duplicate=falseANDstatus!="cancelled".- Exclude all three (spam, duplicate, cancelled). The monthly
metrics.support_ticket_countis the RAW count (includes spam/dup) — do not use it as the "clean" value; recompute from the tickets endpoint. - Verification: train_002 rose 0.375→0.500 when monthly support_tickets were switched from raw to clean.
NPS — latest non-retracted
latest_nps= score of the most recent (maxresponse_date) NPS response withretracted=falsewithin the date range. Ignoreretracted=true.- Per-month
nps_scorein metrics may be null whensurvey_status=missing.
Overdue receivables
overdue_balance=ar_aging.61_90 + ar_aging.90_plusfor the customer matched to the account (older aging buckets only; NOT 1_30/31_60, NOTcurrent).- AR-aging records are keyed by
customer_name(legal name), NOT account_id.
Legal-name CRM match (exact)
- A/R customer links to a CRM account ONLY when
ar_aging.customer_name==account.legal_nameEXACTLY (case- and punctuation-sensitive). Do NOT match onaccount_aliasesor fuzzy/partial names. Near-miss customer names (e.g. "Quartz Insurance Claims Ltd." vs legal "Quartz Insurance PLC"; "Globex North Subsidiary LLC" vs "Globex North Holdings LLC") areunlinkedwithaccount_id=null.
Ranking / tie-break (when a risk score is computed)
- Order by
risk_score DESC, thencurrent_arr DESC, thenaccount_id ASC.
Tenure → churn relationship: NEGATIVE
tenure_risk_direction="negative"(higher tenure lowers churn risk).tenure_coefficient_direction(churn model) ="negative".
Deterministic precision
- Currency: 2 decimals. Percentages: 1 decimal. Counts: integers.
- Churn probabilities: 3 decimals (0.000–1.000, i.e. 0–1 scale).
- Dates:
YYYY-MM-DDorYYYY-MMexactly as templated.
reason_code / action pairing (controlled vocab)
reason_codes: overdue_receivable | low_tenure_high_churn | sla_degradation | nps_drop | usage_decline | renewal_window | expansion_offset | clean_billings.
primary_action / outreach_action: executive_qbr | collections_followup | technical_recovery | renewal_save | nurture_monitor | (no_action, risk-queue only).
Pairing:
overdue_receivable→collections_followuprenewal_window/renewal_risklifecycle /low_tenure_high_churn→renewal_savesla_degradation/usage_decline(no overdue, no renewal_risk) →technical_recovery- clean / low risk →
nurture_monitor(orno_actionin risk-queue top-5 low band) - Strategic account at high risk may merit
executive_qbr.
Risk-signal detection from live metrics (Q2/Q3 months):
overdue_receivable: overdue_balance > 0.low_tenure_high_churn: contract_tenure_months < 24.sla_degradation: any monthsla_compliance < 90, OR last < first−1.nps_drop: latest non-retracted NPS < 30 (and/or declining month-over-month).usage_decline:product_usagelast < first.renewal_window: renewal_date within ±90 days of assessment date, OR lifecycle_status ==renewal_risk.expansion_offset: account has open expansion opportunity pipeline (offsets risk).clean_billings: no risk signals present (healthy account).
risk_level bands (0–100 integer score): critical ≥ 75; high 60–74; medium 40–59; low < 40. (Best-fit; the exact scoring weights are not recoverable from scalar judge feedback — compute a defensible additive composite from the signals above and rank by the tie-break rule.)
2. ARCHETYPE-SPECIFIC RULES
A. Renewal Risk Queue (e.g. train_001 / risk_accounts)
- Review ONLY the listed account_ids; return top 5 by risk_score (tie-break rule above).
- Per account fields:
rank, account_id, risk_score, risk_level, primary_action, current_arr, latest_nps, clean_ticket_count, overdue_balance, reason_codes[]. - portfolio_summary:
accounts_reviewed= N given;critical_or_high_count= count of returned accounts with risk_level in {critical, high};arr_at_risk= sum(current_arr) for critical/high returned accounts;collections_count= #accounts with primary_action=collections_followup;technical_recovery_count= #accounts with primary_action=technical_recovery. - model_checks:
uses_billing_arr_source=true;tenure_risk_direction="negative". - Top-level keys: risk_accounts, portfolio_summary, model_checks, policy_codes.
B. QBR Metrics Packet (e.g. train_002 / qbr_metrics)
- Single account, 3 months (YYYY-MM). Per-month:
revenue= metricsrecognized_revenue(2dp);support_tickets= CLEAN count for that month (from tickets endpoint, NOT raw metrics count);sla_compliance_pct= metricssla_compliance(1dp);nps_score= metricsnps_score(may be null). - highlights:
average_revenue= mean of 3 months;peak_revenue_month/peak_revenue= max;max_sla_month/max_sla_pct= max sla;peak_nps_month/peak_nps_score= max non-null nps;ticket_trend∈ {improving, worsening, flat} (clean ticket trend: improving if last < first). - metric_sources (source-enum lineage):
revenue=billing_snapshot,support_tickets=support_export,sla_compliance=sla_report,nps=nps_survey. - review_plan:
review_due_datefrom template;needs_technical_signoff=falsefor healthy accounts (SLA≥90 and no usage decline);review_owner=customer_successfor CS-owned QBR.- Verification: flipping needs_technical_signoff→true dropped train_002 0.500→0.375 (false was correct).
- agenda_topics: exactly 4 ordered enums; default healthy QBR = [partnership_overview, q2_metrics, performance_highlights, q3_initiatives] (swap in technical_recovery/commercial_expansion only when the account shows that need).
C. Receivables + Pipeline Ops Review (e.g. train_003)
- Start from ALL A/R customers (ar-aging as_of the given as-of date) with
61_90 + 90_plus > 0→ overdue clients. - financial_summary:
overdue_client_count= #overdue customers;overdue_total= Σ(61_90+90_plus);linked_followup_count/unlinked_followup_count= counts by exact legal-name match. - overdue_followups: one per overdue customer, sorted by customer_name ASC;
fields:
customer_name, link_status("linked"|"unlinked"), account_id(linked→id, unlinked→null), overdue_balance(=61_90+90_plus), due_date(given follow-up date),primary_action=collections_followup. - pipeline_summary (opportunities in the quarter window):
won_count/won_revenue= stage "Closed Won";lost_count= "Closed Lost";open_count/open_pipeline= state "open" (stages Discovery/Prospecting/Proposal/Negotiation);win_rate_pct= won/(won+lost)*100 (1dp);top_open_product_line= open product_line with the largest Σ amount. - ops_context:
hr_headcount= Σ headcount across all region rows for the quarter (omit region param);unpaid_claims_total= Σ unpaid_claims_amount;event_orders= event_performance.event_orders (total);event_revenue= event_performance.event_revenue (2dp). - Top-level keys: financial_summary, pipeline_summary, overdue_followups, ops_context, policy_codes.
D. Churn Model Validation + Outreach Ranking (e.g. train_004)
CHURN MODEL (deterministic — retrain on the exports):
- Algorithm: sklearn
LogisticRegression(default solver lbfgs, C=1.0). - Preprocessing pipeline:
- numeric features →
StandardScaler - categorical features →
OneHotEncoder(drop="first", handle_unknown="ignore") - (drop="first" is required — drop=None changes accuracy to 91.7% and breaks the expected 93.3%.)
- numeric features →
- Features (19): tenure, MonthlyCharges, TotalCharges, Contract, PaymentMethod, PaperlessBilling, Partner, Dependents, OnlineSecurity, OnlineBackup, DeviceProtection, TechSupport, StreamingTV, StreamingMovies, SupportTickets90d, NPSLast, UsageTrendPct, InvoicePastDue, ActiveSeatRatio. (customer_id is NOT a feature.)
- Train on churn_train.csv (180 rows, Churn label Yes/No; stratification not required — class imbalance Yes=28/No=152). Validate on churn_validation.csv.
- Expected validation accuracy: 93.3% (56/60) →
accuracy_band="90_plus". tenure_coefficient_direction="negative"(LR coefficient on standardized tenure is ≈ −0.15).- model_validation: training_rows=180, validation_rows=60, feature_count=19, accuracy_pct=93.3, accuracy_band="90_plus", tenure_coefficient_direction="negative".
OUTREACH RANKING:
- Predict churn probability (predict_proba class 1) for the listed candidate accounts (from churn_candidates.csv, 19 features, no label). Rank by predicted probability DESC; return top 5.
- predicted_churn_probability: 3 decimals (0–1 scale).
- With default C=1.0, a reference ordering for the train_004 candidate set was: acct_tandemworks (0.284) > acct_northstar_retail (0.022) > acct_quartz_insure (0.018) > acct_northstar_finance (0.010) > acct_globex_north (0.009). (Probabilities are sensitive to C/solver; always retrain with the exact spec above — the default config is canonical.)
- risk_ranking per row:
rank, customer_id, predicted_churn_probability, outreach_action, reason_code(single reason_code). - outreach_action by reason: overdue_receivable(part-due InvoicePastDue=Yes)→collections_followup; low_tenure_high_churn→renewal_save; sla_degradation/usage_decline→technical_recovery; clean_billings→nurture_monitor.
- cohort_checks:
past_due_shortlist_count= #candidates with InvoicePastDue=Yes (in the ranked candidate set);low_tenure_shortlist_count= #candidates with tenure<24;average_probability_top5= mean of top-5 probs (3dp). - deployment decision: with 90_plus accuracy →
approve_with_monitoring. - Top-level keys: model_validation, risk_ranking, cohort_checks, model_policy_codes.
E. High-Touch Retention Operations Board (e.g. train_005)
- Build as of the assessment date for the months given (Q2: 2026-04..06), A/R as_of that date, and Q2 open expansion opportunities (close_date within the period). Return ALL listed accounts in retention-board order.
- Per account:
rank, account_id, risk_level, primary_action, current_arr, expansion_pipeline, overdue_balance, next_touch_due_date, reason_codes[].current_arr= billing snapshot as_of the assessment date (see §1).expansion_pipeline= Σ amount of that account's OPEN opportunities with close_date within the analysis period.overdue_balance= 61_90+90_plus (ar-aging as_of assessment date, exact legal-name match).next_touch_due_date= the followup_calendar date for that account's primary_action.
- segment_summary:
strategic_accounts/enterprise_accounts= counts by account.segment;arr_at_risk= Σ current_arr for accounts with risk_level in {critical, high};open_expansion_pipeline= Σ expansion_pipeline for all board accounts;net_revenue_exposure=arr_at_risk − open_expansion_pipeline(VERIFIED: this exact formula, using snapshot current_arr, scores correct). - followup_calendar: map each action → its due date (given per task; train_005 example: collections_followup=2026-07-15, technical_recovery=2026-07-18, renewal_save=2026-07-22, executive_qbr=2026-07-29, nurture_monitor=2026-08-05).
- Top-level keys: action_board, segment_summary, followup_calendar, policy_codes.
3. POLICY CODES
Each archetype's answer template includes a policy_codes (or
model_policy_codes) object with pipe-delimited options. The judge scores the
policy_codes block as a unit (block/all-or-nothing or unscored): individual
code swaps did not move the score in train_003 (3 option-sets → identical
0.850) or train_004 (variations → identical 0.278). Therefore exact values
could not be verified against judge feedback. Always emit the policy_codes
object with one value per code chosen from the template's pipe options.
Best-guess values (semantic reasoning; NOT judge-verified — select from the allowed options in each task's template):
| code | options | best-guess | rationale |
|---|---|---|---|
| risk_model_code | RS-2 | RS-6 | RS-9 | RS-9 | composite risk model |
| arr_source_code | REV-1 | REV-4 | REV-8 | REV-8 | billing_snapshot as-of-date source (vs crm / year-end field) |
| support_hygiene_code | SUP-3 | SUP-8 | SUP-9 | SUP-9 | exclude spam+dup+cancelled |
| action_priority_code | ACT-1 | ACT-5 | ACT-7 | ACT-1 | action-by-reason priority |
| board_sort_code | BORD-1 | BORD-4 | BORD-8 | BORD-4 | score desc, arr desc, id asc |
| exposure_formula_code | EXP-2 | EXP-6 | EXP-9 | EXP-6 | net = arr_at_risk − expansion |
| calendar_policy_code | CAL-3 | CAL-5 | CAL-7 | CAL-5 | action→due-date calendar |
| receivable_trigger_code | RCP-4 | RCP-7 | RCP-9 | RCP-9 | older buckets (61_90+90_plus) |
| crm_match_code | CM-2 | CM-5 | CM-8 | CM-8 | exact legal-name match |
| pipeline_window_code | PW-3 | PW-6 | PW-9 | PW-9 | quarter window (close_date in-period) |
| followup_scope_code | FS-1 | FS-4 | FS-8 | FS-4 | all overdue customers |
| model_protocol_code | MOD-2 | MOD-7 | MOD-9 | MOD-7 | logistic regression protocol |
| probability_scale_code | PRB-1 | PRB-4 | PRB-8 | PRB-1 | 0–1 probability scale (3dp) |
| deployment_rule_code | DEP-3 | DEP-5 | DEP-9 | DEP-5 | 90_plus accuracy → approve_with_monitoring |
| outreach_mapping_code | OUT-2 | OUT-6 | OUT-8 | OUT-6 | churn-risk → outreach action mapping |
If a template differs, map the same convention to the option that best describes the rule above. The deterministic conventions in §1–§2 are the load-bearing, judge-verified part; policy_codes are best-effort.
4. OUTPUT FIELD REFERENCE (quick)
- Renewal risk queue: risk_accounts[{rank, account_id, risk_score, risk_level, primary_action, current_arr, latest_nps, clean_ticket_count, overdue_balance, reason_codes[]}], portfolio_summary{accounts_reviewed, critical_or_high_count, arr_at_risk, collections_count, technical_recovery_count}, model_checks{uses_billing_arr_source, tenure_risk_direction}, policy_codes{risk_model_code, arr_source_code, support_hygiene_code, action_priority_code}.
- QBR: qbr_metrics[{month, revenue, support_tickets, sla_compliance_pct, nps_score}], highlights{average_revenue, peak_revenue_month, peak_revenue, max_sla_month, max_sla_pct, peak_nps_month, peak_nps_score, ticket_trend}, metric_sources{revenue, support_tickets, sla_compliance, nps}, review_plan{review_owner, review_due_date, needs_technical_signoff}, agenda_topics[].
- Receivables+pipeline: financial_summary{overdue_client_count, overdue_total, linked_followup_count, unlinked_followup_count}, pipeline_summary{won_count, won_revenue, lost_count, open_count, open_pipeline, win_rate_pct, top_open_product_line}, overdue_followups[{customer_name, link_status, account_id, overdue_balance, due_date, primary_action}], ops_context {hr_headcount, unpaid_claims_total, event_orders, event_revenue}, policy_codes{receivable_trigger_code, crm_match_code, pipeline_window_code, followup_scope_code}.
- Churn: model_validation{training_rows, validation_rows, feature_count, accuracy_pct, accuracy_band, tenure_coefficient_direction}, risk_ranking [{rank, customer_id, predicted_churn_probability, outreach_action, reason_code}], cohort_checks{past_due_shortlist_count, low_tenure_shortlist_count, average_probability_top5}, model_policy_codes {model_protocol_code, probability_scale_code, deployment_rule_code, outreach_mapping_code}.
- Retention board: action_board[{rank, account_id, risk_level, primary_action, current_arr, expansion_pipeline, overdue_balance, next_touch_due_date, reason_codes[]}], segment_summary{strategic_accounts, enterprise_accounts, arr_at_risk, open_expansion_pipeline, net_revenue_exposure}, followup_calendar{collections_followup, technical_recovery, renewal_save, executive_qbr, nurture_monitor}, policy_codes{risk_model_code, arr_source_code, support_hygiene_code, action_priority_code, board_sort_code, exposure_formula_code, calendar_policy_code}.
5. JUDGE-FEEDBACK READINGS (skill-generation notes)
- The train judge returns only a scalar score (0..1), no per-field detail.
- Deterministic-convention fixes move the score cleanly: clean tickets (train_002 +0.125), ARR source snapshot vs year-end field (train_005 0.190 vs 0.095).
- Risk-score / risk_level / action / reason fields for the risk-queue and retention-board archetypes are block-judged per account (exact object match), so those archetypes stay low when the (unspecified) exact scoring weights are not reproduced — focus on nailing every deterministic field and the tie-break order.
- Policy_codes blocks do not respond to individual code swaps (block-scored); emit a complete, plausible set from the template options.