diff --git a/revenuecat/skills/revenuecat-audiences/SKILL.md b/revenuecat/skills/revenuecat-audiences/SKILL.md index 41d1549..d47e68a 100644 --- a/revenuecat/skills/revenuecat-audiences/SKILL.md +++ b/revenuecat/skills/revenuecat-audiences/SKILL.md @@ -8,8 +8,8 @@ description: > # Audience filters and dashboard links -Use Audiences to filter the user's customers and to share a dashboard link to that set. The same -filters answer "who" questions — they are not a full leaderboard sort. +Use Audiences to filter the user's customers and to share a dashboard link to that set. Filters +select the segment; `sort_by` / `sort_direction` on `preview-audience` order the returned sample. There is no first-class CLI command for audiences. Do not tell the user RevenueCat cannot filter or segment customers. @@ -20,7 +20,8 @@ Audiences filter on the [fields](#fields) below, including: - Product and duration — `latestProduct`, `allPurchasedProductIds`, `latestPurchasedOffering`, entitlements, offers -- Spend and renewals — `totalSpent`, `totalRenewals` (thresholds, not a sort) +- Spend and renewals — `totalSpent` (filter, and `sort_by: total_spent`), `totalRenewals` (filter + only; there is no renewal sort) - Subscription state — `status`, trial, auto-renew intent, ownership - Store, platform, and country — `platform`, `latestStore`, `country`, `storefront` - Dates — first seen, purchases, renewals, expiration, trial, cancellation @@ -31,25 +32,31 @@ Map the question onto those fields, filter, then read a sample. 1. Fetch project-specific values with `get-audience-filter-options` for any field marked project-specific that the question uses (`project_id`, `fields`). -2. Reuse an existing audience from `list-audiences` if one already matches. Otherwise - `create-audience` with the same `groups`/`conditions` rule shape as [the filter rule](#the-filter-rule). - Body is only `name` and `rules`. `create-audience` persists a saved audience, so get explicit - confirmation first. -3. `get-audience` with `expand: ["customer_sample"]`. Sample rows include `total_spent`, status, - and latest product — not every filter field. Rank or name customers only from fields the - sample returned. -4. Number fields like `totalRenewals` are filters, not sample columns. Filter on a high - threshold and report the sample; do not invent a value that was not returned. -5. Say it is a sample of matches, not an exhaustive ranking of every customer. Filtering and a - `customer_sample` cannot prove a global superlative (who renewed or spent the most). After you - try the steps above, say that: you can show high-threshold matches, not name a unique maximum. -6. Share the dashboard link ([constructing a link](#constructing-a-link) or - [linking to a saved audience](#linking-to-a-saved-audience)). +2. Call `preview-audience` with `project_id` and `body.rules` in the [filter rule](#the-filter-rule) + shape (for a saved audience from `list-audiences`, pass `body.audience_uuid` instead). It is + read-only and saves nothing. Do not `create-audience` just to inspect a segment. +3. To rank or name a superlative, pass `sort_by` (`status`, `latest_auto_renew_intent`, + `first_seen_at`, `last_seen_at`, `total_spent`) and `sort_direction` (`asc` default, or `desc`) + as top-level parameters, not inside `body`. `sort_by: total_spent` with `sort_direction: desc` + returns the highest spenders first, so the first `customer_sample` row is the top spender. The + sample is capped (typically 50): it is the top or bottom matches, not every customer. +4. Answer "how many" from `stats` (`total_customers`, `active_subscriptions`, `active_trials`, + `total_revenue` in `currency`), never from the sample size. When `stats.is_approximate` is true, + give counts and revenue as approximate ("about 6,400 customers"). +5. Name customers only from fields `customer_sample` returned (`app_user_id`, `email`, `status`, + `total_spent`, `latest_product_name`, first/last seen). Without `sort_by` the sample is + unordered: say it is a sample of matches, not a ranking. +6. `totalRenewals` is a filter only, not a `sort_by` value or a sample column. Filter on a high + threshold and report the matches; do not invent a renewal count or claim a unique maximum. +7. Share the dashboard link. The `preview-audience` result for `body.rules` includes a line + starting "Dashboard URL for this result"; link that exact URL. Build one yourself + ([constructing a link](#constructing-a-link)) only when no tool result gave you a URL. ## Constructing a link -Two shapes: a filtered `all-customers` link for ad-hoc exploration, and a saved-audience link -after `create-audience`. +Use this only when no tool result gave you a "Dashboard URL for this result" line. Two shapes: a +filtered `all-customers` link for ad-hoc exploration, and a saved-audience link after +`create-audience`. 1. Get the project ID from `list-projects`. For dashboard URLs, **strip the `proj` prefix**. 2. Pick fields and operators from the [field tables](#fields). Do not invent field or operator @@ -74,8 +81,9 @@ https://app.revenuecat.com/projects/{project_id}/customer-lists/all-customers?fi ## Linking to a saved audience -Link with `customer_list_id` — the field `create-audience`, `get-audience`, and `list-audiences` -return alongside `id`: +The `create-audience` and `get-audience` results include a "Dashboard URL for this result" line; +link that exact URL. If you only have `list-audiences` output, link with `customer_list_id`, the +field returned alongside `id`: ``` https://app.revenuecat.com/projects/{project_id}/customer-lists/{customer_list_id} diff --git a/revenuecat/skills/revenuecat-dashboard-links/SKILL.md b/revenuecat/skills/revenuecat-dashboard-links/SKILL.md index 9dcc3a2..d4c83eb 100644 --- a/revenuecat/skills/revenuecat-dashboard-links/SKILL.md +++ b/revenuecat/skills/revenuecat-dashboard-links/SKILL.md @@ -11,6 +11,12 @@ description: Share `https://app.revenuecat.com/...` links so the user can open the page you are talking about. Do not invent IDs. If you do not have one, ask or omit the link. +**Prefer the URL the MCP server returns.** Most RevenueCat MCP tool results about a specific +resource (offerings, products, entitlements, paywalls, experiments, customers, audiences, apps, +billing) include a line starting "Dashboard URL for this result". Link that exact URL. Use the +routes below only when no result gave you one, for example with the `rc` CLI, for a page you did +not fetch through a tool, or for charts. + Via the `rc` CLI (see the `revenuecat-cli` skill): `rc open [section] [id] --print` prints a deep link for a coarse section (`paywalls`, `customers`, `experiments`, `charts`, `apps`, `overview`, `settings`, `integrations`, `catalog`, `api-keys`, `audit-logs`). Use the routes diff --git a/revenuecat/skills/revenuecat-experiment-analysis/SKILL.md b/revenuecat/skills/revenuecat-experiment-analysis/SKILL.md index 87cba73..a6e1c07 100644 --- a/revenuecat/skills/revenuecat-experiment-analysis/SKILL.md +++ b/revenuecat/skills/revenuecat-experiment-analysis/SKILL.md @@ -1,9 +1,9 @@ --- -name: experiment-analysis +name: revenuecat-experiment-analysis description: Use when the user asks to analyze or understand a RevenueCat experiment or its results. --- -To analyze an experiment, follow the following steps. Make sure to execute all steps in this order, do not skip any. More details on the step below. If you already have partial information (eg. the experiment ID), you may skip that step only. Continue following all of the other steps. +To analyze an experiment, follow the following steps. Make sure to execute all steps in this order, do not skip any. More details on the step below. If you already have partial information (eg. the experiment ID), you may skip that step only. Continue following all of the other steps. Within a step, issue independent calls together so they run in parallel. 1. Get the experiment ID 2. Get experiment details @@ -25,6 +25,7 @@ Get the experiment setup / metadata by using the `get-experiment` RevenueCat too Relevant information to extract: - Offerings (offering_a, offering_b, ...): these define the variants of the experiment. More sophisticated setups might also include a `placements` object which defines different offerings per placement (eg. onboarding, feature gate, ...). Offerings include details on the products offered. Products include an `indicative_price`. Note that only USD prices for the US are returned to provide an understanding of overall pricing levels. Any introductory price is not returned. For price localization tests, tests scoped explicitly outside of the US, or introductory prices, this will not provide the full picture, you might have to resort to the `get-product-store-state` tool instead (see the `revenuecat-store-state` skill). Offerings may also include a `paywall_id`, if the offering uses a RevenueCat Paywall. If `paywall_id` is `null`, that means the app is using a custom paywall. +- Product fields are RevenueCat catalog data, not store state: `indicative_price` and the `subscription` fields (`trial_duration`, `duration`) reflect what RevenueCat last synced from the store and can be stale. Before presenting a trial, intro offer, or price from these fields as fact, or flagging one as an experiment confound, confirm with `get-product-store-state` and cross-check the results' trial metrics. - notes: Any notes that were provided about the experiment - display_name: Name of the experiment - targeting_conditions: the experiment only applies to customers meeting these conditions @@ -39,7 +40,7 @@ Relevant information to extract: ## Step 2b: Paywalls -For any paywall_id in any offering of the experiment, use the `render-paywall-screenshot` tool (parameters `project_id`, `paywall_id`) to look at the paywall and understand the differences. +For any paywall_id in any offering of the experiment, use the `render-paywall-screenshot` tool (parameters `project_id`, `paywall_id`, `version: "published"`) to look at the paywall and understand the differences. Pass `version: "published"` so you see what customers were served; the tool defaults to the draft, which may contain unpublished edits. Skip this step if every offering uses a custom paywall (`paywall_id` is `null`). # Step 3: Get supporting chart data @@ -69,9 +70,16 @@ Parameters: - experiment_id - platform (optional), filter results by platform, eg. `ios` - country (optional), filter results by country, eg. `us` -- exposure_status (optional), filter experiment results by exposure status. One of `enrolled`, `exposed`, `not_exposed`. Only applicable for experiments for existing customers. Defaults to "enrolled" (all enrolled customers) when not provided. +- exposure_status (optional), filter experiment results by paywall view status. Use `exposed` for customers who viewed a paywall or `not_exposed` for customers who did not view a paywall. Omit this parameter to include all enrolled customers. This filter is available for both enrollment modes. + +The results can include a metric named `Exposed customers`. Refer to it as `Paywall viewers` when talking to the user. # Step 5: Interpret results Understanding what changes were made in the experiment, now interpret the results. Use your best judgment. +If the user disputes a claim you derived from catalog metadata, verify it yourself with `get-product-store-state` rather than asking the user to check the store. + +# Step 6: Report your overall findings + +Report your overall findings back to the user, with links to the relevant charts and dashboard entities (see the `revenuecat-dashboard-links` skill). diff --git a/revenuecat/skills/revenuecat-experiments/SKILL.md b/revenuecat/skills/revenuecat-experiments/SKILL.md index f107687..55527b3 100644 --- a/revenuecat/skills/revenuecat-experiments/SKILL.md +++ b/revenuecat/skills/revenuecat-experiments/SKILL.md @@ -15,9 +15,9 @@ Keep the variants identical except for the one aspect under test, otherwise resu **Control.** When the control variant is "what production does today", pass the current offering (`is_current` in `list-offerings`) directly as `offering_a_id` — do not duplicate it. Duplicate only when control itself needs changes relative to production. -**Treatments.** First check with `list-offerings` whether an offering already exists that matches what the treatment should serve, and reuse it instead of duplicating. Otherwise, start each treatment from a duplicate of the offering closest to it (usually the control offering). `duplicate-offering` copies the packages (attaching the same existing products) and, if the source offering has a paywall, also copies it as a new unpublished draft; it returns the new offering, including its new `paywall_id`. Then apply the one change under test: +**Treatments.** First check with `list-offerings` whether an offering already exists that matches what the treatment should serve, and reuse it instead of duplicating. Otherwise, start each treatment from a duplicate of the offering closest to it (usually the control offering). `duplicate-offering` copies the packages (attaching the same existing products) and, if the source offering has a paywall, also copies it as a new unpublished draft; it returns the new offering, including its new `paywall_id`. Pass `source_paywall_version: published` so the treatment copies the paywall customers see in production rather than unfinished draft edits; this fails when the source paywall was never published, so use the default (`draft`) only then. Then apply the one change under test: -- Paywall change (copy, layout, CTA, pricing display, ...): this requires the app to use RevenueCat Paywalls — a paywall is an optional property of an offering, and if the control offering has none (`paywall_id` is `null`). Otherwise, load `revenuecat-paywall-design` and edit the duplicated paywall with `edit-paywall-ai`. Request only the minimum changes required to measure the desired effect, and verify the result by looking at the resulting paywall. +- Paywall change (copy, layout, CTA, pricing display, ...): this requires the app to use RevenueCat Paywalls — a paywall is an optional property of an offering. If the control offering has none (`paywall_id` is `null`), the app renders its own paywall in code, so the paywall change has to be made in the app rather than through RevenueCat; tell the user. Otherwise, load `revenuecat-paywall-design` and edit the duplicated paywall with `edit-paywall-ai`. Request only the minimum changes required to measure the desired effect, and verify the result by looking at the resulting paywall. - Price, trial, or introductory-offer change: these live on store products, so the treatment offering needs different products. Check with `list-products` whether suitable products already exist; create only the missing ones and their store counterparts (see the `revenuecat-store-state` skill). Then swap them into the duplicated offering's packages: detach the copied products with `detach-products-from-package`, then attach the new ones with `attach-products-to-package`. - Brand-new paywall: duplicate the offering with `include_paywall: false`, then load `revenuecat-paywall-design` and call `create-paywall-ai` with the new offering's `offering_id` so the generated draft is attached to it directly. A paywall and an offering pair 1:1 — `attach-offering-to-paywall` fails if either side is already paired; undo a wrong pairing with `detach-offering-from-paywall` (unpublish the paywall first if it is published).