Forecasting
Use revenuecat-charts for chart mechanics and metric interpretation. This skill covers turning
them into a projection. Prefer MCP get-chart-options-schema / get-chart-data over
rc charts show — the CLI surface does not expose expiration-month segmentation and selectors
this playbook needs.
Pick the section that matches the ask:
- Month-by-month MRR/ARR (including solve-for-spend, and "in one month" / "in N months") → churn line + Steps below — short horizons are N steps of that line, not a net-movement trend
- Revenue to end of month/year → Rest-of-period run-rates (do not run the MRR Steps)
- Low/base/high or "N months out" → Scenario forecasts (use the churn line when projecting MRR/subs)
- Underspecified ("forecast", "predict the future", no metric/horizon) → default to a 12-month MRR projection and run the MRR Steps; say that default out loud. On annual-heavy books the Status snapshot will not fill every forecast month — use the expiration months it has, leave empty annual months at zero due (do not invent fill-in), keep rolling short-duration due and inflows, and say that the annual schedule only covers the live book's next cycle.
The churn line
For month-by-month MRR or ARR — including solving for the spend or new subscriptions needed to hit a target, including when the user already gave you churned MRR, spend, or efficiency numbers, and including short asks like "what will MRR be in one month" or "in two months" — churned MRR in a month is:
churned MRR[m] = MRR up for renewal in m × (1 − renewal rate)
Short horizons (1–2 months, or any small N)
"In one month" / "in two months" / "by next month" is still the churn line — N forward steps, not a rest-of-period run-rate and not an average of recent net MRR movement.
- Read Status
expiration_monthTotal MRR for every calendar month that overlaps the window (e.g. remaining current month + next month for ~30 days out; two full months for "in two months"). - Each step:
churn[m] = due[m] × (1 − rate)per duration; roll P1M/P3M due past the snapshot; leave empty annual months at zero due. - Add inflows from recent new/resub/expansion only.
- Step the stock:
MRR[m+1] = MRR[m] − churn[m] + inflow[m]— once for N=1, twice for N=2, … - One script. Net movement / frozen churned MRR may appear as a diagnostic gap only — never as a
second method that sets the headline number, and never
annual_base / 12.
Correct — one month ahead (same idea for N=2 with two months in the loop):
# due from Status expiration_month (measure=mrr), not base/12
due_annual_next = 28316.52 # e.g. Oct Total MRR for P1Y
due_monthly = monthly_base # standing P1M book
churn = due_annual_next * (1 - r_annual) + due_monthly * (1 - r_monthly) + ...
mrr_next = mrr_now - churn + inflow_run_rate
Get the renewal rate from Subscription Retention or Cohort Explorer (chart literals only) —
not from observed_churned ÷ base, not from Set-to-Renew %. The rate applies to the due slice
for that duration (for monthlies due ≈ stock; for quarterlies and longer terms, never take
churned ÷ full base and then apply it to the due slice — that understates churn).
Get "MRR up for renewal" from Subscription Status for the current cycle of the live book:
get-chart-data(
project_id="<id>",
chart_name="subscription_status",
realtime=true,
resolution="<id from options schema>",
start_date="<YYYY-MM-DD>",
end_date="<YYYY-MM-DD>",
segment="expiration_month",
selectors="{\"measure\": \"mrr\"}", # JSON string, not an object; not active_subscriptions
limit_num_segments=24, # so later months are not folded into Other
# optional: filters="[{\"name\": \"product_duration\", \"values\": [\"P1Y\"]}]"
)
Use the same get-chart-data pattern for Retention / Cohort Explorer / MRR / MRR Movement — but
do not copy Status-only fields (segment=expiration_month, limit_num_segments,
selectors.measure) onto those charts; take segment/filters/selectors from each chart's options
schema (get-chart-options-schema with "realtime": true).
Each segment is a calendar month when currently active subscriptions' periods end. Use Total
MRR only as due[m]. Set to Renew / Cancel / Billing Issue are near-term intent labels — not
forecast renewal rates. Status is a snapshot of the live book only — still model new / resub /
expansion from MRR Movement (or the user's spend plan) as inflows. If you cannot pull due at all,
ask for the renewal schedule rather than using annual_base / 12.
Due and rate by duration
| Duration | Due (Status available) | Due (Status unavailable) | Rate |
|---|---|---|---|
| P1Y | Total MRR by expiration_month (first + repeat of the live book). Empty far months stay empty — do not invent fill-in. | Prior-year P1Y new + resub in M (misses repeat anniversaries — state that) | Retention Y1 / Cohort (subscription_type = new when using Retention) |
| P1M | Near-term expiration buckets, then roll ≈ standing monthly base each later month | Standing monthly base | Retention / Cohort (or movement churned / due where due ≈ base) |
| P3M / other fixed | Near-term buckets, then roll ≈ base / term-months | base / term-months | Retention / Cohort (or movement churned / due, never churned / full base) |
Wrong — annual due as a flat fraction of the stock:
annual_due = annual_base / 12 # or due_frac = {"annual": 1/12}
Wrong — back-solving a residual so modeled churn matches observed:
repeat_annual = observed_annual_churn - annual_due[m] * (1 - rate)
churn_annual = annual_due[m] * (1 - rate) + repeat_annual # then projected
Wrong — fixed-term rate on the full stock:
rate = 1 - churned_P3M / p3m_base # then churn = (p3m_base/3) * (1 - rate) → understates
Wrong — second model after a gap (blended / net movement / "calibrated" churn):
# after due*(1-rate) disagrees with last month's churned MRR:
project(net_fn) or mrr *= (1 - blended_churn) + inflow # never
Correct:
# due: subscription_status, measure=mrr, segment=expiration_month, filter product_duration=P1Y
annual_due = {"2026-11": 17.32, "2027-02": 6.66, ...} # Total MRR per expiration month
# rate: Subscription Retention Y1 or Cohort Explorer — not Set-to-Renew %
churn_annual[m] = annual_due[m] * (1 - renewal_rate_annual)
# monthly: expiration-month for the next cycle, then due ≈ standing monthly_base thereafter
churn_monthly[m] = monthly_due[m] * (1 - renewal_rate_monthly)
# quarterly: due ≈ p3m_base/3; rate from Retention (or churned_P3M / due, not / p3m_base)
Steps (month-by-month MRR/ARR only)
Do not use these steps for rest-of-period revenue run-rates — use that section instead.
- Pull MRR segmented by
product_durationfor the current stocks. - Pull Subscription Status as in the
get-chart-dataexample above (selectors{"measure": "mrr"},segment=expiration_month, highlimit_num_segments), filtered byproduct_durationfor each duration you model. Read Total MRR per expiration month into the script asdueonly. Roll P1M/P3M due past the snapshot per the table; leave empty annual months empty. - Pull rates from Subscription Retention or Cohort Explorer — not Set-to-Renew % from status. Every rate literal in code must appear in a prior tool output.
- Pull MRR Movement for new / resub / expansion run-rates only (inflows). Do not feed
churned MRR, net movement, or a blended churn rate into the projection. Churn in the script
must be
due[m] × (1 − rate)— neverproject(net_fn)/ a fit on net MRR movement /mrr * (1 - blended_churn). - Compute in exactly one script shaped like the Correct block. If month-1 modeled churn ≠ observed, print the gap as a diagnostic only — do not run a second, calibrated, blended, or net-movement projection, and do not add a residual. The renewal-due result is the central answer.
- Present that central case, the range and what drives it, and the gap diagnostic if any. Call a number a floor or ceiling only if every stated assumption biases it that way.
Rest-of-period run-rates
For "revenue until the end of the month/year" and similar — not the MRR Steps above:
- Today is incomplete. Exclude it from the daily average, or say that you excluded it.
- Give a range or state the observed day-to-day volatility. A bare point estimate is not a forecast.
- Triangulate two methods — elapsed-day pace, and the prior period's shape applied to the current one — and say which you weighted and why.
- Prefer splitting out MRR already locked via Subscription Status expiration months in the remainder of the period, rather than assuming renewals are spread evenly.
- Put the headline numbers in the chat answer; do not defer them to a chart or artifact.
Scenario forecasts
For low / base / high cases or "N months out":
- Each case names the driver that differs (acquisition rate, renewal rate, a pricing change working through the base), not a multiplier on the same number.
- Commit to a central case; a range alone does not answer the question.
- Every renewal or retention rate in the simulation comes from a chart pull for this account. Published benchmarks are context, not inputs.
- "N months out" ends N months from today. If the model is anchored on the last complete month, say so and name the end month.
- When the metric is MRR or subscribers, use the churn line above for the retention side.
Prediction Explorer
Prediction Explorer models cohort LTV, not churn. Use it for payback or LTV cross-checks when the question asks for them; it does not replace the churn line above.