diff --git a/revenuecat/skills/revenuecat-charts/SKILL.md b/revenuecat/skills/revenuecat-charts/SKILL.md index 634d1bf..5e7e0dd 100644 --- a/revenuecat/skills/revenuecat-charts/SKILL.md +++ b/revenuecat/skills/revenuecat-charts/SKILL.md @@ -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": "", "values": ["", ...]}`. - Values within one entry are ORed; separate entries are ANDed. Example: App Store revenue in the @@ -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. @@ -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 @@ -225,8 +250,13 @@ 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 @@ -234,7 +264,8 @@ A rate only describes the population in its denominator. Before presenting one, 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 @@ -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 @@ -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: diff --git a/revenuecat/skills/revenuecat-store-state/SKILL.md b/revenuecat/skills/revenuecat-store-state/SKILL.md index b1ff436..67bbdaf 100644 --- a/revenuecat/skills/revenuecat-store-state/SKILL.md +++ b/revenuecat/skills/revenuecat-store-state/SKILL.md @@ -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 --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. @@ -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`. @@ -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[].other_regions_config.usd_price` (and `eur_price` if the euro zone matters) so Google fills unlisted regions; use `regional_configs[].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[]` 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: diff --git a/revenuecat/skills/revenuecat-troubleshoot/SKILL.md b/revenuecat/skills/revenuecat-troubleshoot/SKILL.md index 7003f49..beeac76 100644 --- a/revenuecat/skills/revenuecat-troubleshoot/SKILL.md +++ b/revenuecat/skills/revenuecat-troubleshoot/SKILL.md @@ -1,6 +1,6 @@ --- name: revenuecat-troubleshoot -description: Diagnose and resolve RevenueCat integration issues — inspects dashboard configuration through the RevenueCat MCP, walks the SDK debug logs, and covers code-side gotchas. Use when the user says offerings are empty, products not loading, entitlement not active after purchase, paywall won't load, transactions not appearing, customer info shows no entitlements, sandbox purchase not working, or RevenueCat is broken on iOS, Android, Kotlin Multiplatform, Flutter, or React Native. +description: Diagnose and resolve RevenueCat integration issues — inspects dashboard configuration through the RevenueCat MCP, walks the SDK debug logs, and covers code-side gotchas. Use when the user says offerings are empty, products not loading, entitlement not active after purchase, paywall won't load, transactions not appearing, customer info shows no entitlements, sandbox purchase not working, an SDK-gated RevenueCat feature not working, or RevenueCat is broken on iOS, Android, Kotlin Multiplatform, Flutter, or React Native. Also use when asked to check or validate the store / product setup. --- # revenuecat-troubleshoot: diagnose RevenueCat integration problems @@ -34,9 +34,9 @@ Walk these nine items in order. Most reports are resolved by steps 1 through 5. 2. **Verify the API key platform matches the app.** iOS apps must use an `appl_…` public SDK key. Android apps must use `goog_…` (or `amzn_…` for Amazon). A mismatched key produces an authentication error on the first network call. On iOS this surfaces as an `INVALID_CREDENTIALS` error code. On Android it surfaces as `PurchasesErrorCode.InvalidCredentialsError`. Use the `list-app-public-api-keys` RevenueCat MCP tool to list the API keys for the project. 3. **Verify the bundle ID / package name matches the one set up in the RevenueCat project.** List the apps using the `list-apps` RevenueCat MCP tool. The `bundle_id` (iOS / App Store) or `package_name` (Android / Play Store / Amazon Appstore) registered there must match the built app exactly, including capitalization. A mismatch causes offerings to come back empty because the app is not recognized. -4. **Verify offerings in the RevenueCat project.** List the project's offerings using the `list-offerings` RevenueCat MCP tool, passing the parameter `expand=items.package.product`. The offering marked with `is_current: true` must have at least one package attached, and each package must reference a store product. An offering with zero packages returns an empty `availablePackages` list even though `getOfferings` succeeds. +4. **Verify offerings in the RevenueCat project.** List the project's offerings using the `list-offerings` RevenueCat MCP tool, passing `limit: 100` and `expand=items.package.product`. The default page of 20 can omit the current offering, and a partial page looks the same as a project with no current offering. The offering marked with `is_current: true` must have at least one package attached, and each package must reference a store product. An offering with zero packages returns an empty `availablePackages` list even though `getOfferings` succeeds. 5. **Verify store products are live.** Products must be in "Ready to Submit" on App Store Connect or "Active" on Google Play Console. A product in a draft state will not be returned by the store, even in sandbox. If the SDK logs show offerings arriving from RevenueCat but products failing to resolve, this is almost always the cause. Use the `get-product-store-state` RevenueCat MCP tool to understand the state of product in App Store Connect or Google Play Console (field `store_status`). To fix or change the store state (not just read it), follow the `revenuecat-store-state` skill. -6. **Verify the testing account.** iOS: the device must be signed into a Sandbox Apple ID under Settings → App Store → Sandbox Account (set on iOS 14+ after the first sandbox prompt). Android: the tester's Gmail must be added to Google Play Console → Setup → License testing, and the app must be installed via the Internal Testing opt-in link, not sideloaded. +6. **Verify the testing account.** iOS: the device must be signed into a Sandbox Apple ID under Settings → App Store → Sandbox Account (set on iOS 14+ after the first sandbox prompt). Android: the tester's Gmail must be added to Google Play Console → Setup → License testing, and the app must be installed from a Play testing track's opt-in link, not sideloaded (see the Android checklist below for which track). 7. **Verify the network.** Corporate VPNs, captive portals, and some DNS filters silently block the RevenueCat API or the store APIs. Try a different network before digging deeper. 8. **Verify the appUserID.** If `logIn(appUserID)` was called with an ID that does not match what the user expects, entitlements appear missing because they are attached to a different RC user. Print `Purchases.shared.appUserID` (iOS) / `Purchases.sharedInstance.appUserID` (Android) and confirm it matches. 9. **Reset and retry.** Uninstall the app, re-sign into the sandbox / tester account, reinstall from the correct channel, relaunch. @@ -71,9 +71,11 @@ get-product-store-state - [ ] Products exist for each store item. - [ ] Store identifiers match App Store Connect / Play Console exactly. - [ ] Product types are correct (subscription vs one-time). -- [ ] Play Store: using `product_id:base_plan_id` format. +- [ ] Play Store: subscriptions use `product_id:base_plan_id` format; one-time products use `product_id` only. - [ ] Store State: `store_status.status` = `ok`. +A `null` duration from `list-products` does not mean the product is misconfigured (it can simply not have been purchased yet). Judge store setup from `get-product-store-state`. + #### Check 3: Entitlements ``` list-entitlements @@ -85,8 +87,8 @@ get-products-from-entitlement (for each entitlement) #### Check 4: Offerings ``` -list-offerings -list-packages +list-offerings (limit: 100) +list-packages (for the offering with is_current: true) ``` - [ ] At least one offering exists with `is_current: true`. - [ ] Packages contain products. @@ -99,6 +101,12 @@ list-webhook-integrations - [ ] Webhook URL is correct and accessible. - [ ] Environment matches (production vs sandbox). +#### Check 6: SDK compatibility (if an SDK-gated feature misbehaves) +Paywall components, experiment enrollment, exposure tracking, Customer Center behavior, In-App Currencies, and offering features depend on minimum SDK versions. Load the `revenuecat-sdk-compatibility` skill before making any SDK-version claim. +- [ ] The feature's gate was checked with `list-sdk-feature-gates`. +- [ ] Any claim about the project's SDK adoption was checked with `list-sdk-versions`. +- [ ] The report separates required SDK versions from versions observed in the project. + ### Phase C: report and offer fixes ``` @@ -212,8 +220,10 @@ Log emoji indicators: 🍎 Apple/StoreKit · 🤖 Google Play · 📦 Amazon · ### iOS -- [ ] Paid Applications agreement signed in App Store Connect. +- [ ] Paid Applications agreement signed in App Store Connect, banking details complete, and the agreement not expired (check its status; changes take up to 24h to propagate). +- [ ] Apple Developer Program membership active, with a valid payment method and auto-renew on. - [ ] In-App Purchase Key uploaded to RevenueCat (StoreKit 2 / SDK 5.x+). +- [ ] App-Specific Shared Secret added to RevenueCat as well; missing either credential can stop products and offerings from loading. - [ ] Products show "Ready to Submit" or "Approved" status. - [ ] Bundle ID matches exactly in Xcode, App Store Connect, and RevenueCat. - [ ] New products: wait 24h for propagation. @@ -223,7 +233,7 @@ Log emoji indicators: 🍎 Apple/StoreKit · 🤖 Google Play · 📦 Amazon · - [ ] App published to at least closed testing track (internal testing won't work). - [ ] Test account added as licensed tester in Play Console. - [ ] Service account credentials (JSON) uploaded to RevenueCat with Finance permissions. -- [ ] Subscriptions use `product_id:base_plan_id` format. +- [ ] Subscriptions use `product_id:base_plan_id` format; one-time products use `product_id` only. - [ ] New products: wait 24h for propagation. ## Reference: App Store rejection troubleshooting