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
50 changes: 29 additions & 21 deletions revenuecat/skills/revenuecat-audiences/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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}
Expand Down
6 changes: 6 additions & 0 deletions revenuecat/skills/revenuecat-dashboard-links/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 12 additions & 4 deletions revenuecat/skills/revenuecat-experiment-analysis/SKILL.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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).
4 changes: 2 additions & 2 deletions revenuecat/skills/revenuecat-experiments/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
Loading