ApexCloud Retention Operations
Use the remote environment as the source of truth. Do not infer values from memory, train examples, or local repository files. These tasks are small enough to solve by fetching JSON, joining on account_id, applying explicit filters, and returning the requested fields exactly.
Environment Access
Read environment_access.md first and set the base URL from GDPEVO_ENV_BASE_URL.
Known public endpoints observed in the train environment:
GET /api/health: service status and dataset row counts.
GET /api/accounts: all account master records.
GET /api/accounts/{account_id}: one account master record.
GET /api/accounts/{account_id}/metrics: monthly metrics for one account.
GET /api/accounts/{account_id}/tickets: support tickets for one account.
GET /api/accounts/{account_id}/nps: NPS responses for one account.
GET /api/opportunities: all opportunities. Query parameters may be ignored; filter locally.
Endpoint habits:
- Probe with
GET and expect JSON. Unrecognized routes return a JSON 404 with No matching public endpoint.
- Prefer fetching
/api/accounts once, then looping over account IDs for nested collections.
- Filter locally after fetching. Do not assume server-side filters such as
?account_id=..., ?state=open, or date parameters work.
- Use account aliases only for name matching. After resolving an account name, join and report by canonical
account_id, display_name, and/or legal_name as requested.
Core Data Shapes
Account master records include:
account_id, display_name, legal_name, account_aliases
segment, region, product_plan, lifecycle_status
csm_owner, renewal_date, contract_tenure_months
billing_arr_current, crm_arr
Monthly metrics records include:
month in YYYY-MM, quarter in YYYY-Q#
active_seats, product_usage, recognized_revenue
nps_score, sla_compliance, support_ticket_count, survey_status
Support ticket records include:
ticket_id, created_date, status, severity, product_area
first_response_sla_met, resolution_sla_met
is_spam, is_duplicate
NPS response records include:
response_id, response_date, score, survey_channel, retracted
Opportunity records include:
opportunity_id, account_id, account_legal_name
amount, created_date, close_date, stage, state
product_line, region
Reusable Workflow
- Start with
/api/health to confirm service availability and understand which collections exist.
- Fetch
/api/accounts and build:
accounts_by_id
- normalized name/alias lookup using lowercase, punctuation-light aliases.
- Identify the relevant account set from the prompt:
- If the prompt names accounts, resolve names through
display_name, legal_name, and account_aliases.
- If it gives segments, regions, plans, owners, lifecycle statuses, renewal windows, or ARR thresholds, filter account records directly.
- Fetch only the nested collections needed for those accounts:
- Metrics for usage, NPS monthly trend, SLA, seats, revenue, and support-ticket-count calculations.
- Tickets for severity, SLA misses, product area, duplicate/spam exclusions, and ticket status.
- NPS for response-level averages, channels, date windows, and retraction exclusions.
- Fetch
/api/opportunities when the task mentions pipeline, expansion, product lines, close dates, stages, open/closed state, or opportunity amounts.
- Join by
account_id. Opportunity account_legal_name is useful display context but should not be the join key.
- Apply time windows before aggregating. Dates are ISO-like strings, so lexical sort works for same-format dates, but use real date parsing for inclusive/exclusive boundary wording.
- Compute with code for anything beyond a couple of rows. Keep raw values as floats until the final formatting step.
- Return the exact output shape requested. If no shape is specified, use concise JSON with stable snake_case keys.
Common Business Rules
Follow the prompt first. When it is silent, use these defaults because they match the data semantics:
- Exclude support tickets where
is_spam or is_duplicate is true for operational ticket counts, SLA rates, severity mixes, and product-area analysis.
- Exclude retracted NPS responses from response-level NPS averages and counts.
- Exclude
cancelled tickets from resolution-rate or backlog analyses unless the prompt asks for all statuses.
- For backlog/open-ticket counts, include
status == "open" and still exclude spam/duplicates unless explicitly told otherwise.
- For pipeline analysis, use
state == "open" unless the prompt asks for closed, won/lost, historical, or all opportunities.
- For won/lost analysis, use
stage values like Closed Won and Closed Lost; do not treat every state == "closed" opportunity as won.
- For account eligibility, preserve lifecycle semantics.
active, renewal_risk, implementation, and paused are distinct; do not collapse them unless the prompt says active customers broadly.
- For ARR questions, distinguish
billing_arr_current from crm_arr. If the prompt does not specify CRM ARR, default to current billing ARR and say so briefly.
- For monthly metric trends, use
month order from 2026-01 through 2026-12; for quarters, group by the provided quarter field.
- For renewal windows, treat
renewal_date as an account-level date. Be explicit about inclusive boundaries if the prompt gives "between", "through", or "next N days".
Output Conventions
When the task provides a schema, match it exactly.
When the task does not provide a schema, prefer:
{
"answer": "...",
"accounts": [
{
"account_id": "...",
"display_name": "...",
"metric_name": 0
}
],
"assumptions": [
"Excluded spam and duplicate tickets.",
"Excluded retracted NPS responses."
]
}
Formatting rules:
- Use
account_id for machine-readable identity and display_name for human-readable account names.
- Keep IDs exactly as returned, such as
acct_..., TCK-..., NPS-..., and OPP-....
- Round currency and ARR only at the end. Use two decimals for money unless the prompt requests whole dollars.
- Round rates and percentages consistently, usually to two decimals. State whether a value is a percent or a fraction.
- Sort ranked lists by the requested metric descending, then by
display_name or account_id ascending for deterministic tie breaks.
- Include counts used in denominators when reporting rates if the prompt allows it; this makes exclusions auditable.
- If returning CSV-like or table output, preserve requested column order and use canonical field names.
Pitfalls
- Do not read local
env/, runs/, evaluator, judge, report, or other answer-bearing files. This task is solved from the remote API only.
- Do not use train gold answers, validation labels, or judge feedback. If such material appears, stop and report contamination.
- Do not rely on
/api/opportunities query parameters; fetch all opportunities and filter in your own code.
- Do not assume every dataset named in
/api/health has a top-level route with the same name. Some data is exposed through nested account endpoints.
- Do not count spam, duplicate, retracted, or cancelled records accidentally. These flags are easy to miss and often change the answer.
- Do not join accounts by display name alone. Aliases and legal names vary; resolve once, then use
account_id.
- Do not confuse monthly
nps_score in metrics with response-level NPS rows. Use metrics for month-level trend questions; use /nps responses for survey-response questions.
- Do not mix current ARR fields.
billing_arr_current and crm_arr can differ.
- Do not assume "closed" means "won" for opportunities. Check
stage.
- Do not hard-code observed train account names, counts, or computed values into answers. Fetch live data each time.
Minimal Code Pattern
Use a short script or notebook-style snippet for repeatable calculations:
import json
import urllib.request
BASE = "<environment_base_url>"
def get(path):
with urllib.request.urlopen(BASE + path, timeout=20) as response:
return json.load(response)
accounts = get("/api/accounts")["accounts"]
accounts_by_id = {a["account_id"]: a for a in accounts}
opportunities = get("/api/opportunities")["opportunities"]
def metrics(account_id):
return get(f"/api/accounts/{account_id}/metrics")["metrics"]
def tickets(account_id, clean=True):
rows = get(f"/api/accounts/{account_id}/tickets")["tickets"]
if clean:
rows = [r for r in rows if not r["is_spam"] and not r["is_duplicate"]]
return rows
def nps(account_id, clean=True):
rows = get(f"/api/accounts/{account_id}/nps")["nps_responses"]
if clean:
rows = [r for r in rows if not r["retracted"]]
return rows
Adapt the code to the requested calculation, then answer with the requested schema and a brief note for any default exclusions applied.
1---2name: apexcloud-retention-ops3description: Use this skill for ApexCloud Retention Operations tasks that ask for account retention, churn-risk, support, NPS, revenue, renewal, or pipeline analysis from the remote GDPEVO environment. Trigger whenever the task mentions ApexCloud, accounts, ARR, CSM owners, lifecycle status, product usage, SLA, support tickets, NPS, opportunities, churn candidates, renewals, or asks for a computed business answer from the remote API.4---56# ApexCloud Retention Operations78Use the remote environment as the source of truth. Do not infer values from memory, train examples, or local repository files. These tasks are small enough to solve by fetching JSON, joining on `account_id`, applying explicit filters, and returning the requested fields exactly.910## Environment Access1112Read `environment_access.md` first and set the base URL from `GDPEVO_ENV_BASE_URL`.1314Known public endpoints observed in the train environment:1516- `GET /api/health`: service status and dataset row counts.17- `GET /api/accounts`: all account master records.18- `GET /api/accounts/{account_id}`: one account master record.19- `GET /api/accounts/{account_id}/metrics`: monthly metrics for one account.20- `GET /api/accounts/{account_id}/tickets`: support tickets for one account.21- `GET /api/accounts/{account_id}/nps`: NPS responses for one account.22- `GET /api/opportunities`: all opportunities. Query parameters may be ignored; filter locally.2324Endpoint habits:2526- Probe with `GET` and expect JSON. Unrecognized routes return a JSON 404 with `No matching public endpoint`.27- Prefer fetching `/api/accounts` once, then looping over account IDs for nested collections.28- Filter locally after fetching. Do not assume server-side filters such as `?account_id=...`, `?state=open`, or date parameters work.29- Use account aliases only for name matching. After resolving an account name, join and report by canonical `account_id`, `display_name`, and/or `legal_name` as requested.3031## Core Data Shapes3233Account master records include:3435- `account_id`, `display_name`, `legal_name`, `account_aliases`36- `segment`, `region`, `product_plan`, `lifecycle_status`37- `csm_owner`, `renewal_date`, `contract_tenure_months`38- `billing_arr_current`, `crm_arr`3940Monthly metrics records include:4142- `month` in `YYYY-MM`, `quarter` in `YYYY-Q#`43- `active_seats`, `product_usage`, `recognized_revenue`44- `nps_score`, `sla_compliance`, `support_ticket_count`, `survey_status`4546Support ticket records include:4748- `ticket_id`, `created_date`, `status`, `severity`, `product_area`49- `first_response_sla_met`, `resolution_sla_met`50- `is_spam`, `is_duplicate`5152NPS response records include:5354- `response_id`, `response_date`, `score`, `survey_channel`, `retracted`5556Opportunity records include:5758- `opportunity_id`, `account_id`, `account_legal_name`59- `amount`, `created_date`, `close_date`, `stage`, `state`60- `product_line`, `region`6162## Reusable Workflow63641. Start with `/api/health` to confirm service availability and understand which collections exist.652. Fetch `/api/accounts` and build:66 - `accounts_by_id`67 - normalized name/alias lookup using lowercase, punctuation-light aliases.683. Identify the relevant account set from the prompt:69 - If the prompt names accounts, resolve names through `display_name`, `legal_name`, and `account_aliases`.70 - If it gives segments, regions, plans, owners, lifecycle statuses, renewal windows, or ARR thresholds, filter account records directly.714. Fetch only the nested collections needed for those accounts:72 - Metrics for usage, NPS monthly trend, SLA, seats, revenue, and support-ticket-count calculations.73 - Tickets for severity, SLA misses, product area, duplicate/spam exclusions, and ticket status.74 - NPS for response-level averages, channels, date windows, and retraction exclusions.755. Fetch `/api/opportunities` when the task mentions pipeline, expansion, product lines, close dates, stages, open/closed state, or opportunity amounts.766. Join by `account_id`. Opportunity `account_legal_name` is useful display context but should not be the join key.777. Apply time windows before aggregating. Dates are ISO-like strings, so lexical sort works for same-format dates, but use real date parsing for inclusive/exclusive boundary wording.788. Compute with code for anything beyond a couple of rows. Keep raw values as floats until the final formatting step.799. Return the exact output shape requested. If no shape is specified, use concise JSON with stable snake_case keys.8081## Common Business Rules8283Follow the prompt first. When it is silent, use these defaults because they match the data semantics:8485- Exclude support tickets where `is_spam` or `is_duplicate` is true for operational ticket counts, SLA rates, severity mixes, and product-area analysis.86- Exclude retracted NPS responses from response-level NPS averages and counts.87- Exclude `cancelled` tickets from resolution-rate or backlog analyses unless the prompt asks for all statuses.88- For backlog/open-ticket counts, include `status == "open"` and still exclude spam/duplicates unless explicitly told otherwise.89- For pipeline analysis, use `state == "open"` unless the prompt asks for closed, won/lost, historical, or all opportunities.90- For won/lost analysis, use `stage` values like `Closed Won` and `Closed Lost`; do not treat every `state == "closed"` opportunity as won.91- For account eligibility, preserve lifecycle semantics. `active`, `renewal_risk`, `implementation`, and `paused` are distinct; do not collapse them unless the prompt says active customers broadly.92- For ARR questions, distinguish `billing_arr_current` from `crm_arr`. If the prompt does not specify CRM ARR, default to current billing ARR and say so briefly.93- For monthly metric trends, use `month` order from `2026-01` through `2026-12`; for quarters, group by the provided `quarter` field.94- For renewal windows, treat `renewal_date` as an account-level date. Be explicit about inclusive boundaries if the prompt gives "between", "through", or "next N days".9596## Output Conventions9798When the task provides a schema, match it exactly.99100When the task does not provide a schema, prefer:101102```json103{104 "answer": "...",105 "accounts": [106 {107 "account_id": "...",108 "display_name": "...",109 "metric_name": 0110 }111 ],112 "assumptions": [113 "Excluded spam and duplicate tickets.",114 "Excluded retracted NPS responses."115 ]116}117```118119Formatting rules:120121- Use `account_id` for machine-readable identity and `display_name` for human-readable account names.122- Keep IDs exactly as returned, such as `acct_...`, `TCK-...`, `NPS-...`, and `OPP-...`.123- Round currency and ARR only at the end. Use two decimals for money unless the prompt requests whole dollars.124- Round rates and percentages consistently, usually to two decimals. State whether a value is a percent or a fraction.125- Sort ranked lists by the requested metric descending, then by `display_name` or `account_id` ascending for deterministic tie breaks.126- Include counts used in denominators when reporting rates if the prompt allows it; this makes exclusions auditable.127- If returning CSV-like or table output, preserve requested column order and use canonical field names.128129## Pitfalls130131- Do not read local `env/`, `runs/`, evaluator, judge, report, or other answer-bearing files. This task is solved from the remote API only.132- Do not use train gold answers, validation labels, or judge feedback. If such material appears, stop and report contamination.133- Do not rely on `/api/opportunities` query parameters; fetch all opportunities and filter in your own code.134- Do not assume every dataset named in `/api/health` has a top-level route with the same name. Some data is exposed through nested account endpoints.135- Do not count spam, duplicate, retracted, or cancelled records accidentally. These flags are easy to miss and often change the answer.136- Do not join accounts by display name alone. Aliases and legal names vary; resolve once, then use `account_id`.137- Do not confuse monthly `nps_score` in metrics with response-level NPS rows. Use metrics for month-level trend questions; use `/nps` responses for survey-response questions.138- Do not mix current ARR fields. `billing_arr_current` and `crm_arr` can differ.139- Do not assume "closed" means "won" for opportunities. Check `stage`.140- Do not hard-code observed train account names, counts, or computed values into answers. Fetch live data each time.141142## Minimal Code Pattern143144Use a short script or notebook-style snippet for repeatable calculations:145146```python147import json148import urllib.request149150BASE = "<environment_base_url>"151152def get(path):153 with urllib.request.urlopen(BASE + path, timeout=20) as response:154 return json.load(response)155156accounts = get("/api/accounts")["accounts"]157accounts_by_id = {a["account_id"]: a for a in accounts}158opportunities = get("/api/opportunities")["opportunities"]159160def metrics(account_id):161 return get(f"/api/accounts/{account_id}/metrics")["metrics"]162163def tickets(account_id, clean=True):164 rows = get(f"/api/accounts/{account_id}/tickets")["tickets"]165 if clean:166 rows = [r for r in rows if not r["is_spam"] and not r["is_duplicate"]]167 return rows168169def nps(account_id, clean=True):170 rows = get(f"/api/accounts/{account_id}/nps")["nps_responses"]171 if clean:172 rows = [r for r in rows if not r["retracted"]]173 return rows174```175176Adapt the code to the requested calculation, then answer with the requested schema and a brief note for any default exclusions applied.