Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 45 additions & 12 deletions revenuecat/skills/revenuecat-charts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,33 @@ In general, to avoid clogging the context, start with defined timeframes and lar

## 2. Retrieve chart data with `get-chart-data`

### Choosing dates and resolution

- Relative ranges phrased in weeks, months, or quarters ("last 2 weeks", "last month") are
ambiguous. If today is Friday 2026-07-31, "last 2 weeks" can mean Jul 18–31 (trailing 14 days),
Jul 12–25 (the last two full weeks), or Jul 19–31 (last full week plus the current one). Ask which
the user means, offering each reading with its concrete dates. Skip the question only when the
dates are already pinned down: explicit dates, a range in days, wording that names full calendar
periods ("the previous calendar month"), or an earlier clarification.
- Date ranges are inclusive (start_date and end_date are included in the range). "Last N days" is a
trailing window: end date is today, start date is N-1 days before today.
- Every period is labeled with the first day of the calendar period containing it (week, month, or
quarter start), regardless of `start_date`. Weeks start on the project's configured week start day
(e.g. Sunday or Monday), so week labels vary per project. A range that starts or ends mid-period
returns partial buckets whose labels don't match the requested dates. For trailing windows, prefer
daily resolution — with `aggregate` when you only need totals — so the data covers exactly the
requested dates.
- When presenting results, state the start and end dates you queried, never the first/last bucket
labels. E.g. Jul 18–31 queried weekly (Sunday start) returns buckets labeled Jul 12, 19, and 26 —
say "Jul 18 – Jul 31", not "Jul 12 – Jul 26".

### Calling `get-chart-data`

- Always set `"realtime": true` and specify start date, end date and resolution ID.
- Always follow the guidelines from a prior `get-chart-options-schema` for that chart.
- Consider rate limits: don't query too many charts at once.
- Date ranges are inclusive (start_date and end_date are included in the range). When asked for
data for the "last N days", take that into account (use today as end date, start date is (N-1)
days before today).
- For questions spanning several projects, make one `get-chart-data` call per project and issue
them in parallel batches of about 10–20. Wider batches risk the API rate limit; check each batch
for rate-limited calls and re-issue only those. Don't fall back to one call at a time.
- Use available `filters` to constrain the output. They are a JSON-encoded array of
`{"name": "<filter id>", "values": ["<value id>", ...]}`.
- Values within one entry are ORed; separate entries are ANDed. Example: App Store revenue in the
Expand Down Expand Up @@ -137,6 +156,8 @@ General guidelines:
band, not that the value can't be computed.
- After looking: if nothing in the tools matches, or two readings would produce materially
different numbers, ask the user to define the metric. Do not invent a definition.
- Keep fan-out deliberate on complex questions: start with high-level charts, then ask a clarifying
question (which product, store, or country?) rather than pulling every possible cut.
- When using the data tools, date ranges are inclusive (start_date and end_date are included in the range). When asked for data for the "last N days", take that into account (use today as end date, start date is (N-1) days before today).
- Provide links to RevenueCat charts (see the Dashboard URL Format section below) where it is useful. Provide specific links including filters, segments, date ranges, etc — eg. if you are asked for proceeds in the last 3 months, link to the revenue chart with custom date range of the last 3 months and the `revenue_type` selector set to `proceeds`, don't link to the plain revenue chart
- For forecasts, projections, or run-rates, load the `revenuecat-forecasting` skill before pulling charts.
Expand Down Expand Up @@ -211,11 +232,15 @@ this order before answering:
1. **Quantify the shift.** Pull the chart data, confirm the magnitude and timing.
2. **Check configuration.** Offerings, packages, products, paywalls and experiments are not visible
in metrics, so never infer them from a chart. If your answer names any of them, look it up in
this run:
this run. Hedging ("likely", "worth confirming") or offering ("want me to check?") does not make a
guess acceptable. These checks are all mandatory, not alternatives to pick from:
- `list-experiments` with `status="stopped"` and `status="running"`. If an experiment stopped
near the shift, call `get-experiment-results` to see which variant won.
- `list-offerings` with `limit: 100` (the default page of 20 rarely covers a real project), then
`get-offering` on the `is_current` id with `expand: ["package.product"]`. An offering with
near the shift, call `get-experiment-results` yourself to see which variant won — don't tell
the user to check it.
- `list-offerings` with `limit: 100` and no `expand` (the default page of 20 rarely covers a real
project; never conclude there is no current offering from a partial page), then `get-offering`
on the `is_current` id with `expand: ["package.product"]` — on `get-offering` the values are
`package` and `package.product`, without the `items.` prefix `list-offerings` uses. An offering with
`paywall_id: null` has no RevenueCat paywall — load `revenuecat-paywall-design` before giving
paywall advice.
- `get-product-store-state` before saying a product is retired, unavailable, or no longer
Expand All @@ -225,16 +250,22 @@ this order before answering:
4. **Only then form a hypothesis.** Present it as a hypothesis, not a finding. An unverified guess
about configuration is a missing tool call, never your headline finding.

Do not skip step 2. Once you have made the calls, if their results cannot explain the shift, say
so explicitly rather than constructing a mechanism.
Do not skip step 2. If you are about to write "was pulled from the offering" or "an experiment was
rolled out" without having called `list-experiments` or `list-offerings` in this run, stop and make
the call first. Once you have made the calls, if their results cannot explain the shift — the range
predates the current configuration, or nothing changed — say so explicitly rather than constructing
a mechanism. "The data shows X happened but I cannot determine why from the available
configuration" is a valid answer. An old date range may make the answer inconclusive; it is never a
reason to skip the calls.

## Populations and denominators

A rate only describes the population in its denominator. Before presenting one, check that this is
the population the question is about.

- When one segment dominates the denominator, the blended rate describes that segment, not the app.
Re-query filtered to the population the question is about and lead with that number.
Re-query filtered to the population the question is about and lead with that number, keeping the
blended rate beside it.
- **Never present a rate as evidence while also calling its denominator inflated or
unrepresentative.** Re-query with a filter instead of caveating.
- Report the filtered numbers yourself rather than recommending the user go look at a filtered
Expand All @@ -247,6 +278,8 @@ the population the question is about.
too, except in deliberate period-over-period comparisons.
- If you have a metric for one side of a comparison but not the other, query the missing side with
the same chart and settings before comparing. Do not substitute a value from a different chart.
If you cannot fetch the matching value, present each number on its own, labeled with its chart
and metric, and make no gap or ratio claim.
- For open-ended questions like "how are {segment} users doing?", do not stop at segment-only
metrics. Pull the requested segment and an overall/unfiltered baseline for the key conversion or
revenue-quality metric, then judge performance relative to that baseline. Do not evaluate a
Expand All @@ -255,7 +288,7 @@ the population the question is about.
revenue from all cohorts and renewals. If you cannot get a matching baseline, say so and avoid
directional performance claims.
- When a user is confused that two metrics diverge, say what each one counts before explaining the
gap.
gap. Two metrics only track each other if one is a subset of the other.

Wrong — different charts merged under one header:

Expand Down
22 changes: 17 additions & 5 deletions revenuecat/skills/revenuecat-store-state/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ There is at most one active (non-terminal) plan per project. List plans before c

Always use `get-product-store-state` (CLI: `rc products show <product-id> --store-state`). Never infer store prices, availability, or trials from `get-product`, `list-products`, or offering data — those are RevenueCat catalog, not store state. Surface the response's `warnings`.

If `store_status.status` is `could_not_check`, RevenueCat could not read the store: empty `pricing`, `availability`, or `localizations` mean the read failed, not that nothing is configured. Say the product couldn't be read from the store, without guessing a cause — never say it has no store prices.

## Prerequisites

Store-state writes require store credentials connected to the RevenueCat project (an App Store Connect API key, or a Google Play service account with the **Manage store presence** permission) and a RevenueCat login with write access to the project. If a write fails with a credentials or permission error, report what is missing and how to fix it (dashboard app settings for store credentials) instead of retrying.
Expand All @@ -26,9 +28,10 @@ Apply writes to production stores and can change what live customers see and pay
After planning succeeds:

1. Summarize `summary` and each `plan_items[*].diff` in plain language (before → after per field).
2. Report every warning (`severity`, `field`, `message`). Never apply if any `plan_items[*].warnings` has `severity: blocker`. Fix the desired state, update the plan, and re-plan until there are no item-level blockers.
3. Optionally share the dashboard review URL:
2. Report every warning (`severity`, `field`, `message`) verbatim, from both `warnings` and `plan_items[*].warnings`. Never downgrade a blocker or argue that its validator doesn't apply to this store (not "blocker on territories, but not applicable to Play — applying"). Never apply if any `plan_items[*].warnings` has `severity: blocker`. Fix the desired state, update the plan, and re-plan until there are no item-level blockers.
3. Share the dashboard review URL for every plan you describe — including one whose planning failed (the page shows the errors) and an existing plan that blocks a create:
`https://app.revenuecat.com/projects/{project_id}/product-catalog/product-editor/review-changes?plan_id={plan_id}`
A `draft` has no review page yet; describe its contents instead.
4. Wait for an explicit go-ahead. Confirm each product's change individually or as one clearly itemized batch — never fold unrelated changes into a single confirmation.
5. Only then call apply, and only if `actions` includes `apply`.

Expand All @@ -43,14 +46,23 @@ Reads do not need confirmation. Submitting products to Apple for review is a sep
- Put prices, availability, localizations, and (if the user provided one) the review screenshot in the desired state. Do not call `create-product-prices`, `upload-product-store-state-screenshot`, or `equalize-subscription-prices`.
- App Store subscriptions: send the prices to keep in `territory_prices` and include `common.pricing.equalize_missing_subscription_prices` with that `base_territory` so missing Apple territories are filled at apply. Do not send that field for App Store one-time products.
- App Store review screenshots: omit screenshot so apply can upload a blank placeholder unless the user supplied an image.
- RC Billing / Test Store: pricing is `common.pricing.currency_prices` (create-only; updating an existing currency is rejected).
- RC Billing / Test Store: pricing is `common.pricing.currency_prices` (create-only; updating an existing currency is rejected). There is no equalization, so every currency must come from the user.
- Play Store subscriptions: set `store_state.base_plans[<id>].other_regions_config.usd_price` (and `eur_price` if the euro zone matters) so Google fills unlisted regions; use `regional_configs[<region>].price` only for regions the user wants to override.
- Never invent territory or currency prices or convert them with exchange rates. If the user didn't give a price for a territory, ask whether to fill it from another currency using the store mechanisms above. Every price is `amount_micros` (1 unit = 1,000,000 micros).
- Approved App Store subscriptions (check with `get-product-store-state`): Apple never applies a price or intro-offer change immediately. Set a future `start_date` (the next day works) on each `territory_prices[<territory>]` entry you change and on any intro/trial offer you add — without it Apple rejects the change and apply stops at that territory. Keep using `equalize_missing_subscription_prices` for unpriced territories; it carries the base territory's `start_date` into the gaps. Tell the user when the change takes effect and create the plan in the same turn; the date alone is not a reason to ask before planning.
- Desired states are patches: omitted fields stay unchanged, a `null` map value deletes that entry, and `territories: false` removes availability.
3. **Plan.** `plan-product-store-state-plan`, then poll `get-product-store-state-plan` until status is `planned`, `planned_and_finished`, or `plan_errored`.
4. **Review with the user.** Follow [Confirm before applying](#confirm-before-applying). If planning failed, report `error_message` / per-item errors and the review URL; do not apply.
5. **Apply.** `apply-product-store-state-plan`. Then poll `get-product-store-state-plan` until `applied` or `apply_errored`. Report per-product `apply_status` and any `apply_error_message`. If apply fails because the store changed after planning, tell the user and start a new plan from the fresh state.
6. **Submit for review when ready.** App Store only: after apply succeeded, the change is in App Store Connect but not yet submitted to Apple. Offer `submit-products-to-store` for the products just applied (confirmed write). Skip for Play Store, RC Billing, and Test Store.
5. **Apply.** `apply-product-store-state-plan`. Then poll `get-product-store-state-plan` until `applied` or `apply_errored`. Share the progress URL `https://app.revenuecat.com/projects/{project_id}/product-catalog/product-editor/commit-to-store?plan_id={plan_id}`. If apply looks like it will take more than ~10 minutes, stop polling, give the estimated completion, and point the user to that page (or to ask you again later). Report per-product `apply_status` and any `apply_error_message`. Apply stops at the first failing item and leaves earlier items committed; a retry re-runs only the items that did not apply. If apply fails because the store changed after planning, tell the user and start a new plan from the fresh state.
6. **Submit for review when ready.** App Store only: after apply succeeded, the change is in App Store Connect but not yet submitted to Apple. Offer `submit-products-to-store` for the products just applied (confirmed write). Submit only what the user asked for or what just applied (`apply_status: applied`, or present in `get-product-store-state`), never every product that looks ready. Report the per-product outcomes, including skipped ones; if a submission fails, report why and stop. Skip for Play Store, RC Billing, and Test Store.

To abandon a draft or planned plan, `discard-product-store-state-plan`. To change a draft, `update-product-store-state-plan` then plan again.

## Gotchas

- App Store localizations: name ≤ 30 characters, description ≤ 45. The API accepts longer values and Apple rejects them at apply.
- Store products cannot be deleted. To retire one, set its territories to `false` (and move Play base plans out of `ACTIVE`), then `archive-product` if it should also stop appearing in RevenueCat.

## CLI equivalents

See the `revenuecat-cli` skill for install, auth, and schema discovery. Mapping:
Expand Down
Loading
Loading