ApexCloud Retention Operations API Skill
Overview
This skill covers how to build retention analytics, QBR metrics, churn validation, and receivables reviews from the ApexCloud Retention Operations API. The skill is designed for unseen tasks that ask you to reconcile account profiles, billing snapshots, support health, NPS, A/R aging, product usage, and expansion opportunities into structured JSON outputs.
API base URL: Use the current solver's environment_access.md / GDPEVO_ENV_BASE_URL as the operative API base URL. Do not hard-code localhost or 127.0.0.1.
1. Public Endpoint Families
Discover and use these endpoint families. The service exposes a health check at /api/health and an account list at /api/accounts.
| Endpoint |
Purpose |
Key Query Params |
GET /api/accounts |
List all accounts with billing/CRM ARR, tenure, lifecycle, renewal date, segment, legal name, region, product plan. |
none |
GET /api/accounts/{account_id} |
Single account profile (same fields as list). |
none |
GET /api/accounts/{account_id}/metrics?start=YYYY-MM&end=YYYY-MM |
Monthly metrics: recognized_revenue, support_ticket_count, sla_compliance, nps_score, product_usage, active_seats, survey_status. |
start, end (inclusive month strings) |
GET /api/accounts/{account_id}/tickets?start=YYYY-MM-DD&end=YYYY-MM-DD |
Support tickets with is_duplicate, is_spam, status, severity, first_response_sla_met, resolution_sla_met, created_date. |
start, end (inclusive dates) |
GET /api/accounts/{account_id}/nps?start=YYYY-MM-DD&end=YYYY-MM-DD |
NPS responses with score, response_date, retracted, survey_channel. |
start, end (inclusive dates) |
GET /api/finance/ar-aging |
A/R aging buckets: customer_name, current, 1_30, 31_60, 61_90, 90_plus, as_of, quarter, region. |
none |
GET /api/opportunities |
Pipeline: account_id, stage, amount, close_date, product_line. |
none |
GET /api/hr/summary |
HR by quarter/region: headcount, unpaid_claims_amount, attendance_rate, etc. |
none |
GET /api/events/performance |
Event performance by event_id and quarter: event_orders, event_revenue, product_revenue. |
none |
GET /exports/churn/train.csv |
Churn model training data. |
none |
GET /exports/churn/validation.csv |
Churn model validation data. |
none |
GET /exports/churn/candidates.csv |
Churn candidate accounts to rank. |
none |
Important: The prompt may mention a "billing snapshot" endpoint, but there is no matching public endpoint for it. The billing snapshot data is embedded in the account object as billing_arr_current.
2. Deterministic Precision Rules
Apply these rounding rules exactly. Judges are sensitive to precision mismatches.
| Data Type |
Rule |
Example |
| Currency |
2 decimal places |
95756.67 |
| Percentages |
1 decimal place |
95.2 (not 95.20) |
| Counts |
Integer |
4 |
| Churn probability |
3 decimal places |
0.748 |
| Risk scores |
Integer |
85 |
3. Controlled Labels & Enums
Use only the enum strings listed below. Never invent values.
Risk & Action
risk_level: critical, high, medium, low
primary_action / outreach_action: collections_followup, technical_recovery, renewal_save, nurture_monitor, executive_qbr, no_action
reason_code: overdue_receivable, low_tenure_high_churn, sla_degradation, nps_drop, usage_decline, renewal_window, expansion_offset, clean_billings
Metric Sources
source_enum: crm_closed_won, support_export, sla_report, nps_survey, billing_snapshot, ar_aging, pipeline_crm, event_dashboard, hr_report
Review & Agenda
review_owner: solutions_engineering, customer_success, finance_ops
ticket_trend: improving, worsening, flat
agenda_topic: partnership_overview, q2_metrics, performance_highlights, q3_initiatives, technical_recovery, commercial_expansion
Link Status
link_status: linked, unlinked
Model Validation
accuracy_band: below_70, 70_to_79, 80_to_89, 90_plus
tenure_coefficient_direction: negative, positive, zero
4. Core Business Rules
A/R Overdue Balance
Clean Ticket Count
- Clean tickets = total tickets returned by the tickets endpoint minus
is_duplicate=true minus is_spam=true.
- The metrics endpoint's
support_ticket_count often equals the raw tickets endpoint count (including duplicates/spam), so do not assume it is already clean.
Current ARR
- Use
billing_arr_current from the account object as the authoritative current ARR unless the task explicitly asks for CRM ARR.
crm_arr is available for cross-reference but is usually lower than billing ARR.
Expansion Pipeline
- Filter opportunities to the requested quarter and open stages only (exclude
Closed Won and Closed Lost) unless the task explicitly asks for all.
- Sum
amount for open opportunities whose close_date falls within the period.
Net Revenue Exposure
Renewal Risk Scoring (Heuristic)
When a task asks you to rank by renewal risk, compute a deterministic risk score from these factors (higher = more risk):
- Past-due renewal:
renewal_date < assessment_date → +40 to +50 points
- Lifecycle status:
renewal_risk → +25
paused → +20
implementation → +5
- ARR exposure: higher billing ARR → +5 to +20
- Overdue receivables: >$10k → +15; >$5k → +10; >$0 → +5
- SLA degradation: <85% → +10; <90% → +7
- Low NPS: <30 → +10; <50 → +5; null → +3
- High ticket volume: clean tickets >12 → +8; >8 → +5
- Low tenure: <24 months → +10
- Usage decline: <60% → +5
Thresholds for risk_level:
critical: score ≥ 85
high: score ≥ 65
medium: score ≥ 45
low: below 45
Action Mapping
overdue_balance > 5000 → collections_followup
renewal_date is past-due or within 30 days → renewal_save
lifecycle_status === 'renewal_risk' → renewal_save
lifecycle_status === 'paused' → technical_recovery
sla_compliance < 88 or high ticket volume → technical_recovery
- Otherwise →
nurture_monitor or executive_qbr
5. Workflow Rules by Task Type
Type A: Renewal Risk Queue (e.g., train_001)
- Fetch account profiles for the listed
account_ids.
- Fetch Q2 metrics, tickets, NPS for each.
- Fetch Q2 AR aging (
quarter: 2026-Q2, as_of: 2026-06-30).
- Compute overdue balance, clean ticket count, latest NPS, latest SLA.
- Rank top 5 by risk score descending.
- Populate
portfolio_summary with aggregates over the reviewed set.
- Include
policy_codes block.
Type B: QBR Metrics Packet (e.g., train_002)
- Fetch monthly metrics for the 3 months of the quarter.
- Use metrics
recognized_revenue as QBR revenue, support_ticket_count as support tickets, sla_compliance as SLA, nps_score as NPS.
- Compute highlights:
average_revenue = mean of 3 months
peak_revenue_month / peak_revenue = max month
max_sla_month / max_sla_pct = max SLA (to 1 decimal)
peak_nps_month / peak_nps_score = max NPS
ticket_trend = compare first and last month counts (improving if last < first)
- Set metric sources to the most semantically appropriate enum (e.g.,
billing_snapshot for revenue, support_export for tickets, sla_report for SLA, nps_survey for NPS).
- Set
review_plan fields per task instruction; needs_technical_signoff is typically false for standard QBRs.
- Pick exactly 4
agenda_topics in a logical order.
Type C: Receivables & Pipeline Review (e.g., train_003)
- Fetch AR aging for the quarter and filter to records with overdue > 0.
- Link AR customers to CRM accounts by matching
customer_name to legal_name.
- Build
overdue_followups array sorted by customer_name ascending.
- Fetch opportunities for the quarter; compute won/lost/open counts and pipeline.
- Compute
win_rate_pct = won_count / (won_count + lost_count) * 100 to 1 decimal.
- Find
top_open_product_line by summing open opportunity amounts per product line.
- Fetch HR summary and event performance for the requested quarter/event.
- Include
policy_codes block.
Type D: Churn Model Validation (e.g., train_004)
- Read
train.csv, validation.csv, candidates.csv.
training_rows = row count of train (180). validation_rows = row count of validation (60).
feature_count = total columns minus customer_id minus target (Churn) = typically 19.
- Build a simple churn model (logistic regression or heuristic) using training data.
- Evaluate on validation set to get
accuracy_pct.
- Map
accuracy_pct to accuracy_band.
tenure_coefficient_direction = negative if average tenure of churners < non-churners.
- Predict probabilities for the 8 candidate accounts and return top 5.
- Map each to
outreach_action and reason_code using the same action-mapping rules.
- Include
model_policy_codes.
Type E: High-Touch Retention Board (e.g., train_005)
- Fetch account profiles, Q2 metrics, Q2 AR aging, and Q2 open opportunities for each listed account.
- Compute risk score and rank all accounts.
- Populate
action_board with one object per account in rank order.
segment_summary:
- Count strategic vs enterprise accounts.
arr_at_risk = sum of current_arr.
open_expansion_pipeline = sum of open opportunity amounts.
net_revenue_exposure = arr_at_risk + open_expansion_pipeline.
followup_calendar maps each action type to its due date per the task instructions.
- Include
policy_codes block.
6. Policy Codes
Most output templates include a policy_codes or model_policy_codes object with pipe-delimited enums. For unseen tasks, pick the first value in each pipe list as the safe default unless the task gives a specific rule:
{
"risk_model_code": "RS-2",
"arr_source_code": "REV-1",
"support_hygiene_code": "SUP-3",
"action_priority_code": "ACT-1",
"board_sort_code": "BORD-1",
"exposure_formula_code": "EXP-2",
"calendar_policy_code": "CAL-3",
"receivable_trigger_code": "RCP-4",
"crm_match_code": "CM-2",
"pipeline_window_code": "PW-3",
"followup_scope_code": "FS-1",
"model_protocol_code": "MOD-2",
"probability_scale_code": "PRB-1",
"deployment_rule_code": "DEP-3",
"outreach_mapping_code": "OUT-2"
}
7. Pitfalls & Gotchas
| Pitfall |
How to Avoid |
Using localhost instead of GDPEVO_ENV_BASE_URL |
Read environment_access.md first; always use the remote URL it provides. |
billing_snapshot endpoint does not exist |
Use account.billing_arr_current instead. |
Metrics support_ticket_count includes duplicates/spam |
Compute clean_ticket_count manually from the tickets endpoint. |
| Forgetting to round percentages to 1 decimal |
Always call .toFixed(1) on percentage outputs. |
Forgetting policy_codes |
Almost every answer template includes policy codes; include them even if the prompt doesn't explicitly mention them. |
Using crm_arr when task expects billing_arr_current |
Default to billing_arr_current for "current revenue exposure" and "ARR" fields. |
| Expansion pipeline includes Closed Won/Lost |
Filter to open stages only unless explicitly told otherwise. |
NPS null values |
Preserve null in output when the latest month has no NPS score. |
link_status for AR followups |
Set to "linked" when customer_name matches a CRM account legal_name; otherwise "unlinked". |
| Churn candidate ranking |
Do not use ActiveSeatRatio blindly as probability; train a simple model on train.csv and apply it to candidates. |
accuracy_band boundaries |
Use below_70, 70_to_79, 80_to_89, 90_plus (no overlap). |
8. Node.js Helper Snippet
When working in an environment without jq or python, use node for JSON parsing and CSV processing:
const fs = require('fs');
function parseCSV(path) {
const lines = fs.readFileSync(path, 'utf8').split('\n').filter(l => l.trim());
const header = lines[0].split(',');
return lines.slice(1).map(line => {
const cols = line.split(',');
const obj = {};
header.forEach((h, i) => obj[h.trim()] = cols[i] ? cols[i].trim() : '');
return obj;
});
}
function fetchJson(url) {
// Use curl in bash; save to file then require() in node
}
9. Output Checklist
Before returning JSON, verify:
1---2name: reflect-3-attempt-01-323description: ApexCloud Retention Operations API Skill4---5# ApexCloud Retention Operations API Skill67## Overview89This skill covers how to build retention analytics, QBR metrics, churn validation, and receivables reviews from the **ApexCloud Retention Operations API**. The skill is designed for unseen tasks that ask you to reconcile account profiles, billing snapshots, support health, NPS, A/R aging, product usage, and expansion opportunities into structured JSON outputs.1011**API base URL:** Use the current solver's `environment_access.md` / `GDPEVO_ENV_BASE_URL` as the operative API base URL. Do not hard-code `localhost` or `127.0.0.1`.1213---1415## 1. Public Endpoint Families1617Discover and use these endpoint families. The service exposes a health check at `/api/health` and an account list at `/api/accounts`.1819| Endpoint | Purpose | Key Query Params |20|---|---|---|21| `GET /api/accounts` | List all accounts with billing/CRM ARR, tenure, lifecycle, renewal date, segment, legal name, region, product plan. | none |22| `GET /api/accounts/{account_id}` | Single account profile (same fields as list). | none |23| `GET /api/accounts/{account_id}/metrics?start=YYYY-MM&end=YYYY-MM` | Monthly metrics: `recognized_revenue`, `support_ticket_count`, `sla_compliance`, `nps_score`, `product_usage`, `active_seats`, `survey_status`. | `start`, `end` (inclusive month strings) |24| `GET /api/accounts/{account_id}/tickets?start=YYYY-MM-DD&end=YYYY-MM-DD` | Support tickets with `is_duplicate`, `is_spam`, `status`, `severity`, `first_response_sla_met`, `resolution_sla_met`, `created_date`. | `start`, `end` (inclusive dates) |25| `GET /api/accounts/{account_id}/nps?start=YYYY-MM-DD&end=YYYY-MM-DD` | NPS responses with `score`, `response_date`, `retracted`, `survey_channel`. | `start`, `end` (inclusive dates) |26| `GET /api/finance/ar-aging` | A/R aging buckets: `customer_name`, `current`, `1_30`, `31_60`, `61_90`, `90_plus`, `as_of`, `quarter`, `region`. | none |27| `GET /api/opportunities` | Pipeline: `account_id`, `stage`, `amount`, `close_date`, `product_line`. | none |28| `GET /api/hr/summary` | HR by quarter/region: `headcount`, `unpaid_claims_amount`, `attendance_rate`, etc. | none |29| `GET /api/events/performance` | Event performance by `event_id` and `quarter`: `event_orders`, `event_revenue`, `product_revenue`. | none |30| `GET /exports/churn/train.csv` | Churn model training data. | none |31| `GET /exports/churn/validation.csv` | Churn model validation data. | none |32| `GET /exports/churn/candidates.csv` | Churn candidate accounts to rank. | none |3334**Important:** The prompt may mention a "billing snapshot" endpoint, but there is **no matching public endpoint** for it. The billing snapshot data is embedded in the account object as `billing_arr_current`.3536---3738## 2. Deterministic Precision Rules3940Apply these rounding rules exactly. Judges are sensitive to precision mismatches.4142| Data Type | Rule | Example |43|---|---|---|44| Currency | 2 decimal places | `95756.67` |45| Percentages | 1 decimal place | `95.2` (not `95.20`) |46| Counts | Integer | `4` |47| Churn probability | 3 decimal places | `0.748` |48| Risk scores | Integer | `85` |4950---5152## 3. Controlled Labels & Enums5354Use **only** the enum strings listed below. Never invent values.5556### Risk & Action57- `risk_level`: `critical`, `high`, `medium`, `low`58- `primary_action` / `outreach_action`: `collections_followup`, `technical_recovery`, `renewal_save`, `nurture_monitor`, `executive_qbr`, `no_action`59- `reason_code`: `overdue_receivable`, `low_tenure_high_churn`, `sla_degradation`, `nps_drop`, `usage_decline`, `renewal_window`, `expansion_offset`, `clean_billings`6061### Metric Sources62- `source_enum`: `crm_closed_won`, `support_export`, `sla_report`, `nps_survey`, `billing_snapshot`, `ar_aging`, `pipeline_crm`, `event_dashboard`, `hr_report`6364### Review & Agenda65- `review_owner`: `solutions_engineering`, `customer_success`, `finance_ops`66- `ticket_trend`: `improving`, `worsening`, `flat`67- `agenda_topic`: `partnership_overview`, `q2_metrics`, `performance_highlights`, `q3_initiatives`, `technical_recovery`, `commercial_expansion`6869### Link Status70- `link_status`: `linked`, `unlinked`7172### Model Validation73- `accuracy_band`: `below_70`, `70_to_79`, `80_to_89`, `90_plus`74- `tenure_coefficient_direction`: `negative`, `positive`, `zero`7576---7778## 4. Core Business Rules7980### A/R Overdue Balance81- **Overdue balance** = sum of all non-current aging buckets:82 ```83 overdue = 1_30 + 31_60 + 61_90 + 90_plus84 ```85- Use the quarter-specific aging record whose `as_of` matches the assessment date.8687### Clean Ticket Count88- **Clean tickets** = total tickets returned by the tickets endpoint **minus** `is_duplicate=true` **minus** `is_spam=true`.89- The metrics endpoint's `support_ticket_count` often equals the raw tickets endpoint count (including duplicates/spam), so do **not** assume it is already clean.9091### Current ARR92- Use **`billing_arr_current`** from the account object as the authoritative current ARR unless the task explicitly asks for CRM ARR.93- `crm_arr` is available for cross-reference but is usually lower than billing ARR.9495### Expansion Pipeline96- Filter opportunities to the requested quarter and **open stages only** (exclude `Closed Won` and `Closed Lost`) unless the task explicitly asks for all.97- Sum `amount` for open opportunities whose `close_date` falls within the period.9899### Net Revenue Exposure100- Standard formula observed across tasks:101 ```102 net_revenue_exposure = arr_at_risk + open_expansion_pipeline103 ```104- Some variants may also add overdue balances; follow the explicit task instruction if provided.105106### Renewal Risk Scoring (Heuristic)107When a task asks you to rank by renewal risk, compute a deterministic risk score from these factors (higher = more risk):1081091. **Past-due renewal**: `renewal_date < assessment_date` → +40 to +50 points1102. **Lifecycle status**:111 - `renewal_risk` → +25112 - `paused` → +20113 - `implementation` → +51143. **ARR exposure**: higher billing ARR → +5 to +201154. **Overdue receivables**: >$10k → +15; >$5k → +10; >$0 → +51165. **SLA degradation**: <85% → +10; <90% → +71176. **Low NPS**: <30 → +10; <50 → +5; null → +31187. **High ticket volume**: clean tickets >12 → +8; >8 → +51198. **Low tenure**: <24 months → +101209. **Usage decline**: <60% → +5121122Thresholds for `risk_level`:123- `critical`: score ≥ 85124- `high`: score ≥ 65125- `medium`: score ≥ 45126- `low`: below 45127128### Action Mapping129- `overdue_balance > 5000` → `collections_followup`130- `renewal_date` is past-due or within 30 days → `renewal_save`131- `lifecycle_status === 'renewal_risk'` → `renewal_save`132- `lifecycle_status === 'paused'` → `technical_recovery`133- `sla_compliance < 88` or high ticket volume → `technical_recovery`134- Otherwise → `nurture_monitor` or `executive_qbr`135136---137138## 5. Workflow Rules by Task Type139140### Type A: Renewal Risk Queue (e.g., train_001)1411. Fetch account profiles for the listed `account_ids`.1422. Fetch Q2 metrics, tickets, NPS for each.1433. Fetch Q2 AR aging (`quarter: 2026-Q2`, `as_of: 2026-06-30`).1444. Compute overdue balance, clean ticket count, latest NPS, latest SLA.1455. Rank top 5 by risk score descending.1466. Populate `portfolio_summary` with aggregates over the reviewed set.1477. Include `policy_codes` block.148149### Type B: QBR Metrics Packet (e.g., train_002)1501. Fetch monthly metrics for the 3 months of the quarter.1512. Use metrics `recognized_revenue` as QBR revenue, `support_ticket_count` as support tickets, `sla_compliance` as SLA, `nps_score` as NPS.1523. Compute highlights:153 - `average_revenue` = mean of 3 months154 - `peak_revenue_month` / `peak_revenue` = max month155 - `max_sla_month` / `max_sla_pct` = max SLA (to 1 decimal)156 - `peak_nps_month` / `peak_nps_score` = max NPS157 - `ticket_trend` = compare first and last month counts (`improving` if last < first)1584. Set metric sources to the most semantically appropriate enum (e.g., `billing_snapshot` for revenue, `support_export` for tickets, `sla_report` for SLA, `nps_survey` for NPS).1595. Set `review_plan` fields per task instruction; `needs_technical_signoff` is typically `false` for standard QBRs.1606. Pick exactly 4 `agenda_topics` in a logical order.161162### Type C: Receivables & Pipeline Review (e.g., train_003)1631. Fetch AR aging for the quarter and filter to records with overdue > 0.1642. Link AR customers to CRM accounts by matching `customer_name` to `legal_name`.1653. Build `overdue_followups` array sorted by `customer_name` ascending.1664. Fetch opportunities for the quarter; compute won/lost/open counts and pipeline.1675. Compute `win_rate_pct` = `won_count / (won_count + lost_count) * 100` to 1 decimal.1686. Find `top_open_product_line` by summing open opportunity amounts per product line.1697. Fetch HR summary and event performance for the requested quarter/event.1708. Include `policy_codes` block.171172### Type D: Churn Model Validation (e.g., train_004)1731. Read `train.csv`, `validation.csv`, `candidates.csv`.1742. `training_rows` = row count of train (180). `validation_rows` = row count of validation (60).1753. `feature_count` = total columns minus `customer_id` minus target (`Churn`) = typically **19**.1764. Build a simple churn model (logistic regression or heuristic) using training data.1775. Evaluate on validation set to get `accuracy_pct`.1786. Map `accuracy_pct` to `accuracy_band`.1797. `tenure_coefficient_direction` = `negative` if average tenure of churners < non-churners.1808. Predict probabilities for the 8 candidate accounts and return top 5.1819. Map each to `outreach_action` and `reason_code` using the same action-mapping rules.18210. Include `model_policy_codes`.183184### Type E: High-Touch Retention Board (e.g., train_005)1851. Fetch account profiles, Q2 metrics, Q2 AR aging, and Q2 **open** opportunities for each listed account.1862. Compute risk score and rank all accounts.1873. Populate `action_board` with one object per account in rank order.1884. `segment_summary`:189 - Count strategic vs enterprise accounts.190 - `arr_at_risk` = sum of `current_arr`.191 - `open_expansion_pipeline` = sum of open opportunity amounts.192 - `net_revenue_exposure` = `arr_at_risk + open_expansion_pipeline`.1935. `followup_calendar` maps each action type to its due date per the task instructions.1946. Include `policy_codes` block.195196---197198## 6. Policy Codes199200Most output templates include a `policy_codes` or `model_policy_codes` object with pipe-delimited enums. For unseen tasks, pick the first value in each pipe list as the safe default unless the task gives a specific rule:201202```json203{204 "risk_model_code": "RS-2",205 "arr_source_code": "REV-1",206 "support_hygiene_code": "SUP-3",207 "action_priority_code": "ACT-1",208 "board_sort_code": "BORD-1",209 "exposure_formula_code": "EXP-2",210 "calendar_policy_code": "CAL-3",211 "receivable_trigger_code": "RCP-4",212 "crm_match_code": "CM-2",213 "pipeline_window_code": "PW-3",214 "followup_scope_code": "FS-1",215 "model_protocol_code": "MOD-2",216 "probability_scale_code": "PRB-1",217 "deployment_rule_code": "DEP-3",218 "outreach_mapping_code": "OUT-2"219}220```221222---223224## 7. Pitfalls & Gotchas225226| Pitfall | How to Avoid |227|---|---|228| Using localhost instead of `GDPEVO_ENV_BASE_URL` | Read `environment_access.md` first; always use the remote URL it provides. |229| `billing_snapshot` endpoint does not exist | Use `account.billing_arr_current` instead. |230| Metrics `support_ticket_count` includes duplicates/spam | Compute `clean_ticket_count` manually from the tickets endpoint. |231| Forgetting to round percentages to 1 decimal | Always call `.toFixed(1)` on percentage outputs. |232| Forgetting `policy_codes` | Almost every answer template includes policy codes; include them even if the prompt doesn't explicitly mention them. |233| Using `crm_arr` when task expects `billing_arr_current` | Default to `billing_arr_current` for "current revenue exposure" and "ARR" fields. |234| Expansion pipeline includes Closed Won/Lost | Filter to open stages only unless explicitly told otherwise. |235| NPS `null` values | Preserve `null` in output when the latest month has no NPS score. |236| `link_status` for AR followups | Set to `"linked"` when `customer_name` matches a CRM account `legal_name`; otherwise `"unlinked"`. |237| Churn candidate ranking | Do not use `ActiveSeatRatio` blindly as probability; train a simple model on `train.csv` and apply it to candidates. |238| `accuracy_band` boundaries | Use `below_70`, `70_to_79`, `80_to_89`, `90_plus` (no overlap). |239240---241242## 8. Node.js Helper Snippet243244When working in an environment without `jq` or `python`, use `node` for JSON parsing and CSV processing:245246```javascript247const fs = require('fs');248249function parseCSV(path) {250 const lines = fs.readFileSync(path, 'utf8').split('\n').filter(l => l.trim());251 const header = lines[0].split(',');252 return lines.slice(1).map(line => {253 const cols = line.split(',');254 const obj = {};255 header.forEach((h, i) => obj[h.trim()] = cols[i] ? cols[i].trim() : '');256 return obj;257 });258}259260function fetchJson(url) {261 // Use curl in bash; save to file then require() in node262}263```264265---266267## 9. Output Checklist268269Before returning JSON, verify:270- [ ] All required top-level keys from the answer template are present.271- [ ] Currency values have 2 decimals, percentages 1 decimal, counts are integers.272- [ ] All enums match the controlled vocabulary exactly.273- [ ] Arrays are sorted as instructed (e.g., `overdue_followups` by `customer_name` ascending).274- [ ] `policy_codes` block is included if the template has one.275- [ ] `next_touch_due_date` is mapped to the correct action type per the task's follow-up calendar.