What goes in a renewal forecast dashboard?
A renewal forecast dashboard answers two questions at once: what is up for renewal, and how much the forecast deserves to be believed. The second question is the one most dashboards skip — without a frozen snapshot of the forecast at quarter start, you're grading a number that has been quietly edited all quarter.
Which cards belong on this dashboard?
- ARR up for renewal by quarter, next four quarters
- Forecast category mix (commit, likely, at risk, churn) by ARR
- Renewal rate to date this quarter, by count and by ARR
- At-risk ARR with owner and last touchpoint
- Forecast vs. actual accuracy over the past six quarters
- Expansion pipeline attached to renewals
- Renewals closing in the next 30 days without a scheduled meeting
- Slipped renewals: past close date, still open
What data does this dashboard need?
- Renewal opportunities with close date, ARR, and forecast category
- A frozen forecast snapshot written at the start of each period
- Contract terms from billing, not just the CS tool
- Closed outcomes: renewed, churned, downgraded, expanded
- Account health and usage for the at-risk views
- Expansion or upsell opportunities linked to the renewal record
How do you build it?
- Land CS platform, CRM, billing, and product-usage data in a database and keep account IDs, contract dates, ARR, health-score components, and event timestamps.
- Build account-grain models at the grain this dashboard needs, with health components and revenue definitions as explicit columns.
- Create one saved question per card and certify the shared models and definitions.
- Add dashboard filters for Renewal quarter, forecast category, segment, owner, region.
- Reconcile ARR against billing rather than the CS tool, and show data freshness so every viewer knows how current the syncs are.
Example card SQL
SELECT
date_trunc('quarter', r.close_date)::date AS renewal_quarter,
r.forecast_category,
COUNT(*) AS opportunities,
ROUND(SUM(r.arr_usd), 2) AS arr,
ROUND(
100.0 * SUM(r.arr_usd)
/ NULLIF(SUM(SUM(r.arr_usd)) OVER (
PARTITION BY date_trunc('quarter', r.close_date)
), 0), 1
) AS share_of_quarter_pct
FROM renewal_opportunities r
WHERE r.close_date >= date_trunc('quarter', CURRENT_DATE)
AND r.close_date < date_trunc('quarter', CURRENT_DATE)
+ INTERVAL '12 months'
AND r.stage NOT IN ('closed_renewed', 'closed_churned')
GROUP BY 1, 2
ORDER BY 1, arr DESC;