Free for 30 days on Scale.Start free
Customer renewal and upsell monitor
Every step in the template

Customer renewal and upsell monitor

A daily-updating workspace where every account renewing within 90 days has one row scored Red / Yellow / Green, the CSM gets Slack pings on Red + near-term Yellow, and managers see end-of-quarter risk early enough to act.

Outcome

A daily-updating workspace where every account renewing within 90 days has one row scored Red / Yellow / Green, the CSM gets Slack pings on Red + near-term Yellow, and managers see end-of-quarter risk early enough to act.

Time45 min setup, ongoing ~10 min/day per CSMDifficultybeginnerForVP Customer Success / Head of CS / CS Ops at $5M-$50M revenue companies managing 50-500 named accounts.
How this works

Open it, hand it to your agent, walk the steps.

Paste this to your agent (Claude / Cursor / Codex)
You are the agent running on the "Customer renewal and upsell monitor" template workspace, connected via MCP at your-org/customer-renewal-upsell-monitor.

Your job: every morning at 7 AM, read every account renewing within 90 days, score risk, upsert one Accounts row per account, fire Slack on Red + near-term Yellow, post Status. Never edit CRM fields.

User-loop protocol:
- You propose. The CSM decides. Never edit CRM renewal date, contract value, owner. Those are operator-controlled.
- Daily 7 AM (or "run renewal scan"): query CRM for all companies (or deals, depending on where renewal dates live) with renewal_date within RENEWAL_WINDOW_DAYS (default 90).
- For each account: compute days_to_renewal (renewal_date - today), last_activity_days (today - notes_last_activity), score Risk Tier via Claude.
- Risk Tier rubric:
  - Red: days_to_renewal <= 30 OR last_activity_days >= 45 OR (renewal in the past AND no stage advance)
  - Yellow: days_to_renewal 31-90 AND last_activity_days < 45, OR last_activity_days 20-44
  - Green: days_to_renewal > 30 AND last_activity_days < 20
- Upsert Accounts row by CRM account ID. account_row_mapping.json maps CRM ID to Dock row ID. Existing rows update via PATCH; new accounts POST.
- Resolve owner IDs to names via the Owners API + a local cache. Owner names on the row, not numeric IDs.
- Slack: post on Red OR (Yellow AND days_to_renewal <= ALERT_THRESHOLD_DAYS default 30). Green silently updates Dock.
- End of every run, write 1 paragraph to Status: accounts scanned, Red, Yellow, Green, anything errored.

Don't touch:
- account_row_mapping.json from prior runs (only append or update mappings; never delete).
- CRM fields (renewal_date, contract_value, owner).
- An Accounts row's Risk Tier after the CSM marks it Resolved (Resolved is a CSM-controlled column).

First MCP tool calls:
1. list_surfaces(workspace_slug="customer-renewal-upsell-monitor")
2. list_rows(workspace_slug="customer-renewal-upsell-monitor", surface_slug="accounts")
3. get_doc(workspace_slug="customer-renewal-upsell-monitor", surface_slug="status")
The template · 5 steps

Top to bottom. Each step has tasks, pointers, gotchas.

Confirm renewal date location + property type

15 min

Most CRMs store renewal dates in one of two places: on the Company record (custom property) or on a Deal (closedate of a renewal-type deal). The script paths differ. And the property TYPE (date vs datetime) determines what value the search API expects. The wrong type returns zero results with no error, so this verification is critical.

Tasks
  • Ask the operator: are renewal dates on the Company record or on a renewal-type Deal?
  • If on Company: GET /crm/v3/properties/companies/renewal_date. Check the type field. If date, expect YYYY-MM-DD strings. If datetime, expect epoch milliseconds.
  • If on Deal: pick the renewal stage ID, plan to filter dealstage IN [renewal_stage_ids] AND closedate BETWEEN [window].
  • Confirm CSM ownership tracking. HubSpot: hubspot_owner_id. Salesforce: OwnerId. Pipedrive: user_id. The agent resolves owner IDs to names.
  • Note the field name for last activity. HubSpot: notes_last_activity. Salesforce: LastActivityDate.
Gotchas
  • Property type is the #1 silent failure. Update RENEWAL_DATE_PROPERTY_TYPE at the top of the script to match.
  • Multi-pipeline orgs with renewal on Deals: pick one renewal pipeline per workspace. Mixing makes the rubric wrong.

Wire .env + the Python script

20 min

One script: renewal_monitor.py runs every morning at 7 AM. Reads accounts, scores risk via Claude, upserts Dock rows in place, fires Slack on Red + near-term Yellow. Idempotent via account_row_mapping.json (CRM ID to Dock row ID), so re-runs don't duplicate rows.

Tasks
  • Open Setup guide (doc) and copy renewal_monitor.py into a local folder
  • Run pip install anthropic requests python-dotenv
  • Create .env with DOCK_API_KEY, DOCK_WORKSPACE_SLUG, ANTHROPIC_API_KEY, HUBSPOT_ACCESS_TOKEN, SLACK_WEBHOOK_URL, CLAUDE_MODEL=claude-sonnet-4-6
  • Set RENEWAL_DATE_PROPERTY_TYPE at the top of the script (date or datetime, matching what you verified).
  • Generate a Dock API key at trydock.ai/settings/api
  • Create a Slack incoming webhook for the CSM team channel, paste into SLACK_WEBHOOK_URL
  • Set up the Accounts table column schema explicitly before the first run (see Setup guide doc for the full column list).
Gotchas
  • Set crm.owners.read on the HubSpot Private App token. Without it the script falls back to showing numeric owner IDs.
  • account_row_mapping.json. Don't delete it. Deleting causes the next run to POST every account again, creating duplicates.

Run a first scan + walk through with a CSM

30 min

Before scheduling, run the script manually + walk through the first batch with a senior CSM. They eyeball Red flags + recommended actions, confirm the rubric matches reality, and flag anything that's obviously wrong before the daily cadence starts.

Tasks
  • Run python renewal_monitor.py
  • Open Accounts (table). Confirm one row per account in the renewal window, with Risk Tier + Risk Reason + Action Needed populated.
  • Sort by Risk Tier=Red. Walk through each with the CSM. For each: is the risk reason right? Is the recommended action sensible?
  • If two or more Red accounts look wrong, tune the rubric: raise the activity threshold, narrow the renewal window, soften the language.
  • Re-run. Confirm rows update in place (no duplicates).
Gotchas
  • First-run noise. If activity logging is inconsistent across CSMs, every account scores Red on activity. Surface this in Status so the operator sees the systemic gap.
  • Without product usage data, upsell signals are directional. They're based on ARR, company growth, and activity patterns from the CRM. Treat them as conversation starters, not scored leads.
Agent prompt for this step
Run a first renewal scan. Query CRM for all accounts with renewal_date within 90 days. For each account: compute days_to_renewal + last_activity_days. Score Risk Tier (Red / Yellow / Green) via Claude with the rubric in the prompt. Resolve owner ID to CSM name. Upsert Accounts row by CRM ID (account_row_mapping.json maps CRM ID to Dock row ID). Fire Slack on Red + (Yellow AND days_to_renewal <= 30). Post a Status entry: accounts scanned, Red count, Yellow count, Green count, errored.

Schedule the daily 7 AM scan

10 min

Once first-run quality is good, schedule the daily cadence. The script is idempotent: re-runs update existing rows in place, never duplicate. Before the team starts the day, after overnight activity-logging from the CSM team in other time zones settles.

Tasks
  • Option A, cron: crontab -e, add `0 7 * * * cd /path && source .env && python3 renewal_monitor.py >> renewal_monitor.log 2>&1`
  • Option B, CueAPI: cueapi create --schedule '0 7 * * *' --timezone 'America/New_York' --name 'renewal-upsell-monitor' --handler ./renewal_monitor.py
  • Confirm the next morning: Status has a fresh session entry + Accounts has updated Last Updated timestamps.
Gotchas
  • If CSMs span time zones, 7 AM is your laptop's time zone. Use CueAPI's --timezone flag to pick the team's primary tz.
  • Closed laptop overnight + cron = no run. Switch to CueAPI for cloud-scheduled if your machine isn't always on.

Pin the workspace to the CSM team's daily ritual

5 min one-time

The scan only works if CSMs open the workspace each morning. Pin the Accounts URL into the CS Slack channel + add the daily Red review to the standup. Most CSMs land on the workspace, sort by Risk Tier=Red, work the top 3 actions, mark Resolved as they go.

Tasks
  • Pin the Accounts table URL (trydock.ai/[org]/customer-renewal-upsell-monitor/accounts) to the CS Slack channel
  • Add a 5-min Daily Red Review to the CS team standup. Each CSM walks their Red rows + states the touch they'll make today.
  • Move rows through the CSM-controlled Resolved column as actions land. Agent skips Resolved rows on future runs.
Gotchas
  • If CSMs don't open the workspace, the scan is wasted. Pin the URL in two places (Slack channel + calendar standup event) so the link is unmissable.
  • Resolved is CSM-controlled. The agent never flips it. Reps mark Resolved after the renewal closes or the upsell signal is acted on.
FAQ

Common questions on this template.

Where are renewal dates supposed to live?
Either on the Company record (custom property) or on a renewal-type Deal (closedate). Both paths are supported. The Setup guide doc shows how to verify which one applies to your CRM + how to swap the fetch logic if you go with Deals.
What if my CSMs are inconsistent about logging activities?
Every account will score Red on activity. The Status entry will flag this so the operator sees the systemic gap. The deeper fix is CSM behavior, not script tuning. The rubric env vars (ACTIVITY_RED_DAYS) let you loosen the threshold while you fix the underlying habit.
Does the agent edit my contract values or renewal dates?
No. The agent only reads from the CRM. Contract value, renewal date, and owner are all CSM-controlled fields. The agent writes to Dock surfaces only: Accounts rows, Alerts sections, Status paragraphs.
Can the upsell signals replace product-usage scoring?
No. Without product usage data, the upsell signals are directional: derived from ARR, company growth, activity patterns. Treat them as conversation starters for CSMs, not scored expansion leads. If you have product usage data, the Extending section in Setup guide shows how to feed it into the score prompt.
How do I add a new account to the scan?
You don't. The agent reads every account with a renewal date in the window from the CRM. Adding a renewal date to the CRM (or moving an existing one inside the 90-day window) is the only step needed. The next scan picks it up.

Open this template as a workspace.

We mint a fresh copy in your org with the steps as table rows, the pointers as a separate table, and the brief as a doc. Bring your agents, start checking off boxes.