← RevenueCatCONTENT HISTORY

Update to RevenueCat

Snapshot Sep 30, 2026 · 23:10 UTC · version 2.3.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "revenuecat-audiences",
  "description": "Use before sharing a link to a filtered customer list or audience, and when identifying, filtering, or ranking the user's customers (a segment, who they are, a product or duration, spend, renewals, status, country, attribution).\n",
  "included_files": [],
  "skill_md_contents": "---\nname: revenuecat-audiences\ndescription: >\n  Use before sharing a link to a filtered customer list or audience, and when identifying,\n  filtering, or ranking the user's customers (a segment, who they are, a product or duration,\n  spend, renewals, status, country, attribution).\n---\n\n# Audience filters and dashboard links\n\nUse Audiences to filter the user's customers and to share a dashboard link to that set. The same\nfilters answer \"who\" questions — they are not a full leaderboard sort.\n\nThere is no first-class CLI command for audiences. Do not tell the user RevenueCat cannot filter\nor segment customers.\n\n## Identifying, filtering, or ranking customers\n\nAudiences filter on the [fields](#fields) below, including:\n\n- Product and duration — `latestProduct`, `allPurchasedProductIds`, `latestPurchasedOffering`,\n  entitlements, offers\n- Spend and renewals — `totalSpent`, `totalRenewals` (thresholds, not a sort)\n- Subscription state — `status`, trial, auto-renew intent, ownership\n- Store, platform, and country — `platform`, `latestStore`, `country`, `storefront`\n- Dates — first seen, purchases, renewals, expiration, trial, cancellation\n- Attribution and experiments — media source, campaign, ad group, keywords, price experiment\n- Identity and custom attributes — app user ID, email, locale, `customAttribute:{key}`\n\nMap the question onto those fields, filter, then read a sample.\n\n1. Fetch project-specific values with `get-audience-filter-options` for any field marked\n   project-specific that the question uses (`project_id`, `fields`).\n2. Reuse an existing audience from `list-audiences` if one already matches. Otherwise\n   `create-audience` with the same `groups`/`conditions` rule shape as [the filter rule](#the-filter-rule).\n   Body is only `name` and `rules`. `create-audience` persists a saved audience, so get explicit\n   confirmation first.\n3. `get-audience` with `expand: [\"customer_sample\"]`. Sample rows include `total_spent`, status,\n   and latest product — not every filter field. Rank or name customers only from fields the\n   sample returned.\n4. Number fields like `totalRenewals` are filters, not sample columns. Filter on a high\n   threshold and report the sample; do not invent a value that was not returned.\n5. Say it is a sample of matches, not an exhaustive ranking of every customer. Filtering and a\n   `customer_sample` cannot prove a global superlative (who renewed or spent the most). After you\n   try the steps above, say that: you can show high-threshold matches, not name a unique maximum.\n6. Share the dashboard link ([constructing a link](#constructing-a-link) or\n   [linking to a saved audience](#linking-to-a-saved-audience)).\n\n## Constructing a link\n\nTwo shapes: a filtered `all-customers` link for ad-hoc exploration, and a saved-audience link\nafter `create-audience`.\n\n1. Get the project ID from `list-projects`. For dashboard URLs, **strip the `proj` prefix**.\n2. Pick fields and operators from the [field tables](#fields). Do not invent field or operator\n   names.\n3. For project-specific fields, fetch valid values with `get-audience-filter-options` first.\n4. Assemble the [rule JSON](#the-filter-rule): one group per OR-branch, conditions inside a group\n   for AND, values encoded per [value formats](#value-formats).\n5. Serialize and URL-encode the rule with a short script (see [encoding the rule](#encoding-the-rule))\n   — do not encode by hand.\n6. Append it as the `filters` query param on\n   `https://app.revenuecat.com/projects/{project_id}/customer-lists/all-customers`.\n\n## URL format\n\n```\nhttps://app.revenuecat.com/projects/{project_id}/customer-lists/all-customers?filters={encoded_rule}\n```\n\n- `{project_id}` — short hex ID from `list-projects` with `proj` stripped.\n- Filters only work on the `all-customers` list. The unfiltered Audiences home is\n  `/projects/{project_id}/customer-lists`.\n\n## Linking to a saved audience\n\nLink with `customer_list_id` — the field `create-audience`, `get-audience`, and `list-audiences`\nreturn alongside `id`:\n\n```\nhttps://app.revenuecat.com/projects/{project_id}/customer-lists/{customer_list_id}\n```\n\n**An audience's `id` (`aud…`) and its `customer_list_id` (`list…`) are different identifiers.**\nThe dashboard route only resolves `customer_list_id`; an `aud…` id in that slot renders\n\"Audience not found\".\n\n**Correct:**\n\n```\nhttps://app.revenuecat.com/projects/56965ae1/customer-lists/list7c1f0a2b93\n```\n\n**Wrong** (the audience `id` instead of the `customer_list_id`):\n\n```\nhttps://app.revenuecat.com/projects/56965ae1/customer-lists/audf0269cdf3df84dd2\n```\n\nDo not add a `filters` param to a saved-audience link — the audience carries its own rules. If\nthe response has no `customer_list_id`, link to `/customer-lists` and name the audience rather\nthan guessing an id.\n\n## Offering to save the filtered view\n\nA filtered `all-customers` link is ad-hoc — nothing about it is saved. Say so in one short\nsentence when you share one, and offer to save it: \"This view isn't saved — want me to save it\nas an audience so you can find it later?\"\n\nOffer once per conversation. If they accept, call `create-audience` with the same rule you built\nfor the link, then link to it. Do not offer when the link is already to a saved audience.\n\n## The filter rule\n\nThe `filters` value is URL-encoded JSON with this shape:\n\n```json\n{\n  \"groups\": [\n    {\n      \"conditions\": [{ \"field\": \"platform\", \"operator\": \"is\", \"value\": \"android\" }]\n    }\n  ]\n}\n```\n\n- Conditions within a group combine with AND.\n- Groups combine with OR.\n- `value` is always a JSON string — booleans as `\"true\"`/`\"false\"`, numbers as `\"42\"`, lists as a\n  comma-separated string, date ranges and relative dates as stringified JSON (see\n  [value formats](#value-formats)).\n\n**Correct** (compact JSON, whole value URL-encoded):\n\n```\n?filters=%7B%22groups%22%3A%5B%7B%22conditions%22%3A%5B%7B%22field%22%3A%22platform%22%2C%22operator%22%3A%22is%22%2C%22value%22%3A%22android%22%7D%5D%7D%5D%7D\n```\n\n**Wrong** (raw JSON, spaces, unencoded quotes/braces):\n\n```\n?filters={\"groups\": [{\"conditions\": [...]}]}\n```\n\n## Encoding the rule\n\n```python\nimport json\nfrom urllib.parse import quote\n\nrule = {\n    \"groups\": [\n        {\"conditions\": [{\"field\": \"platform\", \"operator\": \"is\", \"value\": \"android\"}]},\n    ]\n}\n\nprint(quote(json.dumps(rule, separators=(\",\", \":\")), safe=\"\"))\n```\n\n## Fields\n\nThe Audiences preview table shows only **Customer**, **Subscription Status**,\n**Auto-Renewal Status**, **Spent**, and **Latest Purchase**. Other attributes appear in the CSV\nfrom **Export all**, or on a customer's profile.\n\nUse these exact `field` strings. See\n[Audiences](https://www.revenuecat.com/docs/dashboard-and-metrics/audiences).\n\n### Text fields\n\nOperators: `is`, `isNot`, `contains`, `doesNotContain`, `isEmpty`, `isNotEmpty`.\n\n| Field                     | Meaning                           |\n| ------------------------- | --------------------------------- |\n| `customerId`              | App user ID                       |\n| `originalAppUserId`       | Original app user ID              |\n| `email`                   | Email                             |\n| `phoneNumber`             | Phone number                      |\n| `locale`                  | Locale                            |\n| `appVersion`              | App version                       |\n| `sdkVersion`              | SDK version                       |\n| `platformVersion`         | Platform (OS) version             |\n| `projectId`               | Project ID (e.g. `proj1ab2c3d4`)  |\n| `projectName`             | Project name                      |\n| `appConfigId`             | App ID (e.g. `app1ab2c3d4`)       |\n| `appConfigName`           | App name                          |\n| `idfa`                    | IDFA                              |\n| `idfv`                    | IDFV                              |\n| `gpsAdId`                 | GPS ad ID                         |\n| `latestPurchasedOffering` | Latest purchased offering         |\n| `latestOffer`             | Latest offer identifier           |\n| `latestEntitlements`      | Latest entitlement identifiers    |\n| `allPurchasedProductIds`  | All purchased product identifiers |\n\n### Enum fields\n\nOperators: `is`, `isNot`, `isAnyOf`, `isNotAnyOf`, `isEmpty`, `isNotEmpty`.\n\n| Field                    | Values                                                                                                                    |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------- |\n| `platform`               | `iOS`, `android`, `web`, `macOS`, `amazon`, `roku`, `tvOS`, `visionOS`, `watchOS`                                         |\n| `status`                 | `active`, `trialing`, `in_grace_period`, `in_billing_retry`, `paused`, `expired`, `incomplete`, `unknown`                 |\n| `latestStore`            | `app_store`, `play_store`, `promotional`, `mac_app_store`, `stripe`, `amazon`, `roku`, `rc_billing`, `paddle`, `external` |\n| `anyActiveStore`         | Any active store — same store identifiers as `latestStore`                                                                |\n| `latestOwnershipType`    | `PURCHASED`, `FAMILY_SHARED`                                                                                              |\n| `latestOfferType`        | `no_offer`, `free_trial`, `introductory_offer`, `offer_code`, `promotional_offer`, `win_back_offer`, `unspecified_offer`  |\n| `priceExperimentVariant` | `a`, `b`, `c`, `d`                                                                                                        |\n| `country`                | Last seen country — ISO 3166-1 alpha-2 codes (e.g. `US`, `DE`)                                                            |\n| `latestStoreCountry`     | ISO 3166-1 alpha-2 country codes                                                                                          |\n| `storefront`             | Store country — ISO 3166-1 alpha-2 codes                                                                                  |\n| `mediaSource`            | project-specific — [fetch valid values](#fetching-project-specific-values)                                                |\n| `campaign`               | project-specific                                                                                                          |\n| `adGroup`                | project-specific                                                                                                          |\n| `ad`                     | project-specific                                                                                                          |\n| `keyword`                | project-specific                                                                                                          |\n| `creative`               | project-specific                                                                                                          |\n| `priceExperimentId`      | project-specific                                                                                                          |\n| `latestProduct`          | product IDs of the project — fetch valid values                                                                           |\n\n### Boolean fields\n\nOperators: `is`, `isNot`. Value is exactly `\"true\"` or `\"false\"`.\n\n| Field                            | Meaning                                              |\n| -------------------------------- | ---------------------------------------------------- |\n| `hasMadeSandboxPurchase`         | Has made a sandbox purchase                          |\n| `hasMadeNonSubscriptionPurchase` | Has made a non-subscription purchase                 |\n| `latestAutoRenewIntent`          | Auto-renewal status (`true` = set to renew)          |\n| `isCurrentlyTrialing`            | Currently trialing                                   |\n| `isRcPromo`                      | Has been granted an entitlement via RC (promotional) |\n\n### Number fields\n\nOperators: `equal`, `notEqual`, `greaterThan`, `greaterThanOrEqual`, `lessThan`,\n`lessThanOrEqual`, `isEmpty`, `isNotEmpty`. Value is a numeric string, e.g. `\"50\"`.\n\n| Field           | Meaning                  |\n| --------------- | ------------------------ |\n| `totalSpent`    | Total spent              |\n| `totalRenewals` | Total number of renewals |\n\n### Date fields\n\nOperators: `before`, `beforeOrOn`, `on`, `after`, `afterOrOn`, `within`, `between`, `notBetween`,\n`isEmpty`, `isNotEmpty`.\n\n| Field                  | Meaning                        |\n| ---------------------- | ------------------------------ |\n| `firstSeenAt`          | First seen                     |\n| `lastSeenAt`           | Last seen                      |\n| `firstPurchaseAt`      | First purchase                 |\n| `mostRecentPurchaseAt` | Most recent purchase           |\n| `mostRecentRenewalAt`  | Most recent renewal            |\n| `latestExpirationAt`   | Latest expiration              |\n| `trialStartAt`         | Trial start                    |\n| `trialEndAt`           | Trial end                      |\n| `subscriptionOptOutAt` | Most recent cancellation       |\n| `trialOptOutAt`        | Most recent trial cancellation |\n\n### Custom attribute fields\n\nFilter with `customAttribute:{key}` (e.g. `customAttribute:favorite_team`). They use the enum\noperators. Fetch known keys and values with `get-audience-filter-options` — never invent a key.\n\n## Value formats\n\n- `isEmpty` / `isNotEmpty` — set `\"value\": \"\"` (the value is ignored).\n- `isAnyOf` / `isNotAnyOf` — comma-separated string: `\"value\": \"US,CA,MX\"`.\n- `before`, `beforeOrOn`, `on`, `after`, `afterOrOn` — calendar date `\"value\": \"2026-01-31\"`\n  (`YYYY-MM-DD`).\n- `between` / `notBetween` — stringified JSON with exactly `from` and `to`:\n  `\"value\": \"{\\\"from\\\":\\\"2026-01-01\\\",\\\"to\\\":\\\"2026-01-31\\\"}\"` (`from` ≤ `to`).\n- `within` — stringified JSON with exactly `direction`, `value`, `unit`:\n  `\"value\": \"{\\\"direction\\\":\\\"last\\\",\\\"value\\\":30,\\\"unit\\\":\\\"days\\\"}\"`. `direction` is `last` or\n  `next`; `unit` is `minutes`, `hours`, or `days`; `value` is a non-negative integer.\n  `before`/`beforeOrOn`/`after`/`afterOrOn` also accept this relative format (not `on`).\n\n## Fetching project-specific values\n\nFields marked project-specific (`mediaSource`, `campaign`, `adGroup`, `ad`, `keyword`, `creative`,\n`priceExperimentId`, `latestProduct`) and custom attributes only match values that exist in the\nproject's data. Fetch with `get-audience-filter-options`:\n\n- `project_id` (required)\n- `fields` (required, at least one) — any of the eight fields above, `customAttribute:{key}` for\n  one custom attribute, or `customAttribute` to list every custom attribute key with its values.\n\nA custom-attribute entry may come back with `cardinality_exceeded: true` — a value the user\nstated verbatim can still be valid even if it is not in the list.\n\nFixed-value fields (`country`, `platform`, `status`, …) are not served by this tool — use the\ntables above. Never guess project-specific values — a filter on a non-existent value silently\nmatches zero customers.\n\n## Example: building a link\n\nUser wants: \"Android customers acquired through Instagram\" in project `proj56965ae1`.\n\nRule (both conditions in one group — AND):\n\n```json\n{\n  \"groups\": [\n    {\n      \"conditions\": [\n        { \"field\": \"platform\", \"operator\": \"is\", \"value\": \"android\" },\n        { \"field\": \"mediaSource\", \"operator\": \"is\", \"value\": \"Instagram\" }\n      ]\n    }\n  ]\n}\n```\n\nLink:\n\n```\nhttps://app.revenuecat.com/projects/56965ae1/customer-lists/all-customers?filters=%7B%22groups%22%3A%5B%7B%22conditions%22%3A%5B%7B%22field%22%3A%22platform%22%2C%22operator%22%3A%22is%22%2C%22value%22%3A%22android%22%7D%2C%7B%22field%22%3A%22mediaSource%22%2C%22operator%22%3A%22is%22%2C%22value%22%3A%22Instagram%22%7D%5D%7D%5D%7D\n```\n\nUser wants: \"customers on iOS or Android who are currently trialing and were first seen in the\nlast 30 days\".\n\n```json\n{\n  \"groups\": [\n    {\n      \"conditions\": [\n        { \"field\": \"platform\", \"operator\": \"isAnyOf\", \"value\": \"iOS,android\" },\n        { \"field\": \"isCurrentlyTrialing\", \"operator\": \"is\", \"value\": \"true\" },\n        {\n          \"field\": \"firstSeenAt\",\n          \"operator\": \"within\",\n          \"value\": \"{\\\"direction\\\":\\\"last\\\",\\\"value\\\":30,\\\"unit\\\":\\\"days\\\"}\"\n        }\n      ]\n    }\n  ]\n}\n```\n"
}

SHA-256: 259171120d7e5513ff939ac596f2e6c530128af0babf5257f8963024ce13a448