Apps Script example · 8 min read

Churn Risk Flag: Copy-Paste Apps Script Pattern

Working churn risk flag example in Apps Script—copy-paste code, common mistakes, and when to get it built professionally.

CSChurnRisk

Risk flags turn health scores into actionable queues for CSMs.

churnRiskFlag sets High when health < 40 or inactivity > 45 days; Medium for softer thresholds; else Low.

Accounts with MRR ≥ 2000 escalate Medium to High so large logos get attention sooner.

Run after customerHealthScore so health is current.

Need this built? Hire a Google Apps Script developer →

Sheet / project setup

SheetColumnPurpose
CustomersA idCustomer
CustomersB health0–100
CustomersC daysSinceActivityInactivity
CustomersD mrrMonthly revenue
CustomersE churnRiskLow/Medium/High

What this script does

Rule-based churn risk labels with MRR escalation.

Prerequisites

Health and activity freshness; MRR populated.

  • Thresholds agreed
  • Run after health score
  • Filter High for CSM queue

Walkthrough

health 35 → High; health 55 days 10 → Medium.

Edge cases

MRR blank treated as 0 — no escalation.

  • String labels exact
  • Order of ifs matters

How to test

MRR 2500 with Medium inputs becomes High.

Hardening for production

Write riskReason text; create tasks in Asana/Jira via API.

Variations

Numeric risk 1–5 instead of labels.

Full code: churnRiskFlag()

Update health and activity, then run churnRiskFlag() for CSM dashboards.

/**
 * Flag churn risk when health is low or days since activity is high.
 */
const CUST = "Customers";
// A id, B health, C daysSinceActivity, D mrr, E churnRisk

function churnRiskFlag() {
  const sheet = SpreadsheetApp.getActive().getSheetByName(CUST);
  const values = sheet.getDataRange().getValues();
  for (let i = 1; i < values.length; i++) {
    const health = Number(values[i][1]) || 0;
    const days = Number(values[i][2]) || 0;
    const mrr = Number(values[i][3]) || 0;
    let risk = "Low";
    if (health < 40 || days > 45) risk = "High";
    else if (health < 60 || days > 21) risk = "Medium";
    if (mrr >= 2000 && risk === "Medium") risk = "High"; // elevate big logos
    values[i][4] = risk;
  }
  sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
}
  1. Line 15: Hard health threshold for High.
  2. Line 15: Inactivity High trigger.
  3. Line 16: Softer dual thresholds.
  4. Line 17: Escalates Medium→High for large accounts.
  5. Line 5: Column E label.

Deploy this example

  1. 01

    Open Apps Script

    In the bound spreadsheet: Extensions → Apps Script. For standalone projects, create one at script.google.com and link your Sheet by ID.

  2. 02

    Paste and save

    Add a .gs file, paste the code below, rename constants at the top (sheet names, column letters, API property keys), then save.

  3. 03

    Authorize once

    Run the main function from the editor. Accept OAuth scopes when prompted — triggers cannot run until authorization succeeds once.

  4. 04

    Add the trigger

    Triggers → Add trigger → choose the handler function and event (time-driven, on edit, or on form submit). Delete test triggers before production.

Before you run: churn flags

  • 1Health column current
  • 2daysSinceActivity definition documented
  • 3MRR currency consistent
  • 4Thresholds approved by CS
  • 5High-risk view/filter ready
  • 6Test escalation with a sample enterprise row

Frequently asked questions

Same Medium risk on a huge account deserves faster response.

This is rules-based — export features to BigQuery for models later.

Add daysToRenewal as another High trigger.

Filter new High flags vs previous snapshot and MailApp.

Keep English codes for filters; translate in the UI layer.

Tune days thresholds per product; exclude seasonal inactive segments.

Append daily snapshots to ChurnRiskHistory.

Related examples

Want this wired into your real workflow?

I adapt these patterns to your Sheet structure, APIs, and triggers — deployed in your Google account. Fixed-scope quotes from $500 · free 30-min consult · quote within 24 hours.